IAP v2.0 Example Web App
You can download a sample IAP web app here:
To access the sample, start an HTTP server on your local machine. If you have python installed, you can use the python HTTP server:
- Unzip the download file.
 - In a terminal window, navigate into the newly unzipped folder (IAPv2-web-app-sample).
 - Enter 
python -m SimpleHTTPServerto start the python server. 
For additional server options, see this Mozilla article.
To run the sample:
- Open a browser at address http://localhost:8000.
 - Follow the directions on the page to use the app
 
Sample Description
The index.html file in the sample contains the following content:
- Script tags to include the Amazon Services library and the test library. The Amazon Services library provides the IAP v2.0 functionality.
 - Script tags to include the 
buttonclicker.jsfile. - HTML for three buttons. The Button Clicker JavaScript file binds methods to the button actions.
 
The Button Clicker JavaScript file includes the following content:
// This is a simple model-view-controller setup to deal with
// the fact that the onPurchaseResponse can get called at any
// time.
//
// The fact that responses can come at any time is important to
// understand when using the API. Your web app could have been
// shut down prior to a receipt being delivered for instance. The
// next time your application runs the receipt will be delivered
// upon initializing the API in this case.
// Model Data
var state = {
    clicksLeft: 10,
    activeButton: "",
    hasRedButton: false,
    hasGreenButton: false,
    hasBlueButton: false,
    lastPurchaseCheckTime: null
}
// Make the view (HTML) look like the model
function refreshPageState() {
    $("#clicksLeft").text(state.clicksLeft);
    var button = $("#theButton");
    var redButton = $("#redButton");
    var greenButton = $("#greenButton");
    var blueButton = $("#blueButton");
    setClass(redButton, "locked", !state.hasRedButton);
    setClass(greenButton, "locked", !state.hasGreenButton);
    setClass(blueButton, "locked", !state.hasBlueButton);
    setClass(redButton, "active", state.activeButton == "red");
    setClass(greenButton, "active", state.activeButton == "green");
    setClass(blueButton, "active", state.activeButton == "blue");
    if (state.activeButton != "") {
        button.css("background-color", state.activeButton);
    } else {
        button.css("background-color", "gray");
    }
    persistPageState();
}
// Saves the state to localStorage so the next time the app
// runs it's the same as it was last run. Most important
// is remembering it IAP status including the lastPurchaseCheckTime
// which is passed to getPurchaseUpdates.
function persistPageState() {
    localStorage.setItem("state", JSON.stringify(state));
}
// Restore the state from localStorage
function loadPageState() {
    if ("state" in localStorage) {
        // If this is the first run then the defaults we
        // set earlier are what we're going to run with.
        state = JSON.parse(localStorage.getItem("state"));
    }
}
function setClass(element, className, condition) {
    if (condition) {
        element.addClass(className);
    } else {
        element.removeClass(className);
    }
}
// Controller
// Excercises consumables
function buttonPressed() {
    if (state.clicksLeft > 0) {
        state.clicksLeft--;
    } else {
        buyClicks();
    }
    refreshPageState();
}
// Excercises entitlements
function redButtonPressed() {
    if (state.hasRedButton) {
        state.activeButton = "red";
    } else {
        buyButton("sample.redbutton");
    }
    refreshPageState();
}
// Excercises entitlements
function greenButtonPressed() {
    if (state.hasGreenButton) {
        state.activeButton = "green";
    } else {
        buyButton("sample.greenbutton");
    }
    refreshPageState();
}
// Excercises subscriptions
function blueButtonPressed() {
    if (state.hasBlueButton) {
        state.activeButton = "blue";
    } else {
        // You need to buy the specific subscription (complete with term)
        buyButton("sample.bluebutton.subscription.1mo");
    }
    refreshPageState();
}
function buyClicks() {
    if (window.AmazonIapV2 == null) {
        alert("You are out of clicks, however Amazon In-App-Purchasing works only with Apps from the Appstore.");
    } else if (confirm("Buy more clicks?")) {
        //window.AmazonIapV2.purchase('sample.clicks');
        var requestOptions = {
            sku: 'sample.clicks'
        };
        window.AmazonIapV2.purchase(
            function(operationResponse) {
                console.debug(operationResponse.requestId);
            },
            function(errorResponse) {
                console.debug(errorResponse);
            },
            [requestOptions]
        );
    }
}
function buyButton(buttonName) {
    if (window.AmazonIapV2 == null) {
        alert("You cannot buy this button, Amazon In-App-Purchasing works only with Apps from the Appstore.");
    } else {
        //window.AmazonIapV2.purchase(buttonName);
        var requestOptions = {
            sku: buttonName
        };
        window.AmazonIapV2.purchase(
            function(operationResponse) {
                console.debug(operationResponse.requestId);
            },
            function(errorResponse) {
                console.debug(errorResponse);
            },
            [requestOptions]
        );
    }
}
// Handler functions that are called from the callbacks
// purchaseItem will cause a purchase response with one receipt
function onPurchaseResponse(e) {
    var response = e.response;
    if (response.status === 'SUCCESSFUL') {
        handleReceipt(response.purchaseReceipt);
    } else if (response.status === 'ALREADY_PURCHASED') {
        // Somehow we are out of sync with the server, let's refresh from the
        // beginning of time.
        var requestOptions = {
            reset: true
        };
        window.AmazonIapV2.getPurchaseUpdates(
            function(operationResponse) {
                // Handle operation response
                var requestId = operationResponse.requestId;
                console.debug(requestId);
            },
            function(errorResponse) {
                // Handle error response
                console.debug(errorResponse);
            },
            [requestOptions]
        );
    } else if (response.status === 'FAILED') {
        alert('Purchase request is interrupted. Please try again later');
    } else if (response.status === 'INVALID_SKU') {
        alert('Invalid SKU. Please make sure of SKUS configuration.');
    }
    refreshPageState();
}
// getPurchaseUpdates will return an array of receipts
function onPurchaseUpdatesResponse(e) {
    var response = e.response;
    for (var i = 0; i < response.receipts.length; i++) {
        handleReceipt(response.receipts[i]);
    }
    state.lastPurchaseCheckTime = response.offset;
    refreshPageState();
    if (response.hasMore) {
        // In case there is more updates that did not
        // get sent with this response, make sure that
        // we get the rest of them.
        var requestOptions = {
            reset: false
        };
        window.AmazonIapV2.getPurchaseUpdates(
            function(operationResponse) {
                // Handle operation response
                var requestId = operationResponse.requestId;
                console.debug(requestId);
            },
            function(errorResponse) {
                // Handle error response
                console.debug(errorResponse);
            },
            [requestOptions]
        );
    }
}
// In either case, the contents of the receipt are handled in the same way
function handleReceipt(receipt) {
    if (receipt.sku == "sample.redbutton") {
        // Entitlement
        state.hasRedButton = true;
    } else if (receipt.sku == "sample.greenbutton") {
        // Entitlement
        state.hasGreenButton = true;
    } else if (receipt.sku.substring(0, 30) == "sample.bluebutton.subscription") {
        // Subscriptions sometimes return the parent's ID so we compare to the
        // parent's ID
        if (receipt.cancelDate == null) {
            // cancelDate is null if we are in the current period
            state.hasBlueButton = true;
        }
    } else if (receipt.sku == "sample.clicks") {
        // Consumable
        state.clicksLeft += 10;
    }
    //If the fulfillment has yet to occur, then fulfill the receipt.
    //Store the receiptId to keep track of the already fulfilled items,
    //then call notifyFulfillment() for the receiptId with the FULFILLED status.
    //If the fulfillment can not occur because the item was for a previous
    //game state or the game does not support that item, then
    //
    notifyFulfillment(receipt.receiptId);
}
/**
* @function notifyFulfillment
* @description NotifyFulfillment notifies Amazon about the purchase fulfillment.
* @param {String} receiptId receipt id
*/
function notifyFulfillment(receiptId) {
    //fulfillmentResult can be FULFILLED or UNAVAILABLE
    var requestOptions = {
        'receiptId': receiptId,
        'fulfillmentResult': 'FULFILLED'
    };
    window.AmazonIapV2.notifyFulfillment(
        function(operationResponse) {
            // Handle operation response
            console.debug(operationResponse);
        },
        function(errorResponse) {
            // Handle error response
            console.debug(errorResponse);
        },
        [requestOptions]
    );
}
// Setup
function initialize() {
    loadPageState();
    amzn_wa.enableApiTester(amzn_wa_tester);
    refreshPageState();
    // Setup button press handlers
    $("#theButton").click(function() { buttonPressed(); });
    $("#redButton").click(function() { redButtonPressed(); });
    $("#greenButton").click(function() { greenButtonPressed(); });
    $("#blueButton").click(function() { blueButtonPressed(); });
    document.addEventListener('amazonPlatformReady', function() {
        // Ensure we can call the IAP API
        if (window.AmazonIapV2 === null) {
            console.debug('Amazon In-App-Purchasing only works with Apps from the Appstore');
        } else {
            window.AmazonIapV2.addListener('getUserDataResponse', function(resp) {});
            window.AmazonIapV2.addListener('getProductDataResponse', function(data) {});
            window.AmazonIapV2.addListener('purchaseResponse', this.onPurchaseResponse);
            window.AmazonIapV2.addListener('getPurchaseUpdatesResponse', this.onPurchaseUpdatesResponse);
        }
    }.bind(this));
}
$(function() {
    initialize();
});

