IAP v2.0示例网页应用
IAP v2.0示例网页应用
您可以从此处下载示例IAP网页应用:
要访问该示例,请在您的本地计算机上启动HTTP服务器。如果已安装python,则可以使用python HTTP服务器:
- 解压下载的文件。
- 在终端窗口中,导航至新解压的文件夹(IAPv2-web-app-sample)。
- 输入
python -m SimpleHTTPServer以启动Python服务器。
有关其他服务器选项,请查看此Mozilla文章。
如何运行该示例:
- 在浏览器中打开地址http://localhost:8000。
- 按照页面上的说明使用该应用
示例描述
示例中的index.html文件包含以下内容:
- 包含Amazon Services库和测试库的脚本标签。Amazon Services库提供了IAP v2.0功能。
- 包含
buttonclicker.js文件的脚本标签。
- 三个按钮的HTML。Button Clicker JavaScript文件会将方法联接到按钮操作。
Button Clicker JavaScript文件包含以下内容:
// 这是一个简单的模型-视图-控制器设置,用于应对
// “可以随时调用onPurchaseResponse”这一
// 事实。
//
// 在使用API时,知道“响应可能随时出现”
// 这一事实非常重要。例如,在传送收据之前,
// 您的网页应用可能已关闭。
// 在这种情况下,在下次运行应用时,
//将在初始化API后传送收据。
//模型数据
var state = {
clicksLeft: 10,
activeButton: "",
hasRedButton: false,
hasGreenButton: false,
hasBlueButton: false,
lastPurchaseCheckTime: null
}
// 使视图(HTML)看起来像模型
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();
}
// 将状态保存到localStorage,以使应用
// 在下次运行时状态与前次相同。最重要的
// 是记住它的IAP状态,包括传递给getPurchaseUpdates的
// lastPurchaseCheckTime。
function persistPageState() {
localStorage.setItem("state", JSON.stringify(state));
}
// 从localStorage还原状态
function loadPageState() {
if ("state" in localStorage) {
// 如果这是首次运行,则使用
// 之前设置的默认值。
state = JSON.parse(localStorage.getItem("state"));
}
}
function setClass(element, className, condition) {
if (condition) {
element.addClass(className);
} else {
element.removeClass(className);
}
}
// 控制器
// 使用消费品
function buttonPressed() {
if (state.clicksLeft > 0) {
state.clicksLeft--;
} else {
buyClicks();
}
refreshPageState();
}
// 行使权利
function redButtonPressed() {
if (state.hasRedButton) {
state.activeButton = "red";
} else {
buyButton("sample.redbutton");
}
refreshPageState();
}
// 行使权利
function greenButtonPressed() {
if (state.hasGreenButton) {
state.activeButton = "green";
} else {
buyButton("sample.greenbutton");
}
refreshPageState();
}
// 使用订阅
function blueButtonPressed() {
if (state.hasBlueButton) {
state.activeButton = "blue";
} else {
// 您需要购买特定订阅(添加了期限)
buyButton("sample.bluebutton.subscription.1mo");
}
refreshPageState();
}
function buyClicks() {
if (window.AmazonIapV2 == null) {
alert(“您的点击次数已用完,不过亚马逊应用内购买仅适用于亚马逊应用商店中的应用。”);
} else if (confirm(“购买更多点击次数?”)) {
//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(“您无法购买此按钮,亚马逊应用内购买仅适用于亚马逊应用商店中的应用。”);
} else {
//window.AmazonIapV2.purchase(buttonName);
var requestOptions = {
sku: buttonName
};
window.AmazonIapV2.purchase(
function(operationResponse) {
console.debug(operationResponse.requestId);
},
function(errorResponse) {
console.debug(errorResponse);
},
[requestOptions]
);
}
}
// 从回调中调用的处理程序函数
// purchaseItem将导致包含一个收据的购买响应
function onPurchaseResponse(e) {
var response = e.response;
if (response.status === 'SUCCESSFUL') {
handleReceipt(response.purchaseReceipt);
} else if (response.status === 'ALREADY_PURCHASED') {
// 不知何故,我们没有与服务器同步。
// 从起点刷新试试吧。
var requestOptions = {
reset: true
};
window.AmazonIapV2.getPurchaseUpdates(
function(operationResponse) {
// 处理操作响应
var requestId = operationResponse.requestId;
console.debug(requestId);
},
function(errorResponse) {
// 处理错误响应
console.debug(errorResponse);
},
[requestOptions]
);
} else if (response.status === 'FAILED') {
alert('购买请求已中断。请稍后再试。');
} else if (response.status === 'INVALID_SKU') {
alert('SKU无效。请确保SKU配置。');
}
refreshPageState();
}
// getPurchaseUpdates将返回一个收据数组
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) {
// 如果还有更新未跟随
// 此响应发送,请确保
// 我们获得了其余的更新。
var requestOptions = {
reset: false
};
window.AmazonIapV2.getPurchaseUpdates(
function(operationResponse) {
// 处理操作响应
var requestId = operationResponse.requestId;
console.debug(requestId);
},
function(errorResponse) {
// 处理错误响应
console.debug(errorResponse);
},
[requestOptions]
);
}
}
// 在任一情况下,都以相同方式处理收据内容
function handleReceipt(receipt) {
if (receipt.sku == "sample.redbutton") {
// 权利
state.hasRedButton = true;
} else if (receipt.sku == "sample.greenbutton") {
// 权利
state.hasGreenButton = true;
} else if (receipt.sku.substring(0, 30) == "sample.bluebutton.subscription") {
// 订阅有时会返回父级ID,因此我们会与父级ID进行
// 比较
if (receipt.cancelDate == null) {
// 如果我们在当前期间,则cancelDate为null
state.hasBlueButton = true;
}
} else if (receipt.sku == "sample.clicks") {
// 消费品
state.clicksLeft += 10;
}
//如果尚未履行,则履行收据。
//存储receiptId以跟踪已履行的商品,
//然后对状态为FULFILLED的receiptId调用notifyFulfillment()。
//如果由于该商品仅适用于之前的游戏状态
//或游戏不支持该商品而无法进行而无法履行,则
//
notifyFulfillment(receipt.receiptId);
}
/**
* @function notifyFulfillment
* @description NotifyFulfillment通知亚马逊关于购买履行的情况。
* @param {String} receiptId receipt id
*/
function notifyFulfillment(receiptId) {
//fulfillmentResult的状态不能为FULFILLED或UNAVAILABLE
var requestOptions = {
'receiptId': receiptId,
'fulfillmentResult': 'FULFILLED'
};
window.AmazonIapV2.notifyFulfillment(
function(operationResponse) {
// 处理操作响应
console.debug(operationResponse);
},
function(errorResponse) {
// 处理错误响应
console.debug(errorResponse);
},
[requestOptions]
);
}
// 设置
function initialize() {
loadPageState();
amzn_wa.enableApiTester(amzn_wa_tester);
refreshPageState();
// “设置”按钮按下处理程序
$("#theButton").click(function() { buttonPressed(); });
$("#redButton").click(function() { redButtonPressed(); });
$("#greenButton").click(function() { greenButtonPressed(); });
$("#blueButton").click(function() { blueButtonPressed(); });
document.addEventListener('amazonPlatformReady', function() {
// 确保我们可以调用IAP API
if (window.AmazonIapV2 === null) {
console.debug('亚马逊应用内购买只适用于亚马逊应用商店中的应用');
} 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();
});
").insertBefore(this);
}
});
});
$(document).ready(function() {
if ($("#jumpoffset").length) {
var scrollhash = document.getElementById('jumpoffset');
scrollhash.scrollIntoView(true);
if (location.hash) {
location.href = location.hash;
} else {
var scrollbody = document.querySelector('.mainColumn');
scrollbody.scrollIntoView(true);
window.scroll(0, -500);
}
}
});
$(document).ready(function() {
if ($("#jump").length) {
var scrollold = document.getElementById('jump');
scrollold.scrollIntoView(true);
window.scroll(0, -100);
if (location.hash) {
location.href = location.hash;
}
}
});
//resize container on local builds
$(document).ready(function() {
if (window.location.href.indexOf("/127.0.0.1:4000/") > -1) {
document.querySelector('.container-fluid').style.position = 'fixed';
}
});
//add underline to headers
$(document).ready(function() {
if ($(".productTitle").is(':contains("Alexa Skills Kit")')) {
$(".dppnItem a").each(function() {
if ($(this).is(':contains("Alexa Skills Kit")')) {
$(this).css("border-bottom", "1.3px solid #FFFFFF");
}
});
}
if ($(".productTitle").is(':contains("Alexa Presentation Language (APL)")')) {
$(".dppnItem a").each(function() {
if ($(this).is(':contains("Alexa Skills Kit")')) {
$(this).css("border-bottom", "1.3px solid #FFFFFF");
}
});
}
if ($(".productTitle").is(':contains("Alexa Connect Kit")')) {
$(".dppnItem a").each(function() {
if ($(this).is(':contains("Alexa Connect Kit")')) {
$(this).css("border-bottom", "1.3px solid #FFFFFF");
}
});
}
if ($(".productTitle").is(':contains("A/B Testing")')) {
$(".dppnItem a").each(function() {
if ($(this).is(':contains("Alexa Skills Kit")')) {
$(this).css("border-bottom", "1.3px solid #FFFFFF");
}
});
}
if ($(".productTitle").is(':contains("Alexa Smart Properties")')) {
$(".dppnItem a").each(function() {
if ($(this).is(':contains("Alexa Smart Properties")')) {
$(this).css("border-bottom", "1.3px solid #FFFFFF");
}
});
}
if ($(".productTitle").is(':contains("Alexa Conversations Developer Guide")')) {
$(".dppnItem a").each(function() {
if ($(this).is(':contains("Alexa Skills Kit")')) {
$(this).css("border-bottom", "1.3px solid #FFFFFF");
}
});
}
if ($(".productTitle").is(':contains("Alexa Design Guide")')) {
$(".dppnItem a").each(function() {
if ($(this).is(':contains("Alexa Design Guide")')) {
$(this).css("border-bottom", "1.3px solid #FFFFFF");
}
});
$(".dppnItem a").each(function() {
if ($(this).is(':contains("Alexa Design Guide")')) {
$(this).css("padding", "0");
}
});
}
if ($(".productTitle").is(':contains("Alexa Gadgets Toolkit")')) {
$(".dppnItem a").each(function() {
if ($(this).is(':contains("Alexa Gadgets Toolkit")')) {
$(this).css("border-bottom", "1.3px solid #FFFFFF");
}
});
$(".dppnItem a").each(function() {
if ($(this).is(':contains("Alexa Gadgets Toolkit")')) {
$(this).css("padding", "0");
}
});
}
if ($(".productTitle").is(':contains("Alexa Voice Service")')) {
$(".dppnItem a").each(function() {
if ($(this).is(':contains("Alexa Voice Service")')) {
$(this).css("border-bottom", "1.3px solid #FFFFFF");
}
});
}
if ($("h1").is(':contains("Alexa Reference Content")')) {
$(".dppnItem a").each(function() {
if ($(this).is(':contains("Reference")')) {
$(this).css("border-bottom", "1.3px solid #FFFFFF");
}
});
}
if ($("h1").is(':contains("Alexa Videos")')) {
$(".dppnItem a").each(function() {
if ($(this).is(':contains("Videos")')) {
$(this).css("border-bottom", "1.3px solid #FFFFFF");
}
});
}
if ($("h1").is(':contains("Alexa Code Samples")')) {
$(".dppnItem a").each(function() {
if ($(this).is(':contains("Code Samples")')) {
$(this).css("border-bottom", "1.3px solid #FFFFFF");
}
});
}
if ($("h1").is(':contains("Documentation Home")')) {
$(".dppnItem a").each(function() {
if ($(this).is(':contains("Home")')) {
$(this).css("border-bottom", "1.3px solid #FFFFFF");
}
});
}
});
$(document).ready(function () {
const myScrollspy = $("#myScrollspy");
if (myScrollspy.length) {
const containsList2orList3 = myScrollspy.find(".list2, .list3").length > 0;
if (containsList2orList3) {
myScrollspy.css("display", "block");
$(".fa.fa-info-circle").show();
} else {
myScrollspy.css("display", "none");
$(".fa.fa-info-circle").hide();
}
}
});
/**
* This function moves the 'page-not-translated' section from its original position
* to a new position after the 'atd-categories-header' and ensures it is displayed
* only in the new location. This is achieved by hiding the section initially,
* moving it, and then displaying it again once it is in the correct position.
*/
$(document).ready(function() {
const $pageNotTranslatedMessage = $('#page-not-translated');
const $desktopCategoriesHeader = $('.navigation-header.atd-categories-header');
const $mobileToggleHeader = $('.mobile-toggle');
if ($pageNotTranslatedMessage.length) {
if ($mobileToggleHeader.length) {
$pageNotTranslatedMessage.insertAfter($mobileToggleHeader);
} else if ($desktopCategoriesHeader.length) {
$pageNotTranslatedMessage.insertAfter($desktopCategoriesHeader);
}
$pageNotTranslatedMessage.show();
}
});
// Function to handle smooth scrolling to target elements when links are clicked
$(document).ready(function () {
$('.customspy a, .mainColumn a').on('click', function (event) {
const targetId = $(this).attr('href');
if (targetId && targetId.startsWith('#')) {
event.preventDefault();
const targetElement = document.querySelector(`[id="${targetId.replace('#', '')}"]`);
if (targetElement) {
const hash = targetId.substring(1);
window.history.pushState(null, '', `#${hash}`);
targetElement.scrollIntoView(true);
window.scroll(0, -500);
}
}
});
});
window.addEventListener('DOMContentLoaded', function () {
const targetId = window.location.hash;
if (targetId) {
const targetElement = document.querySelector(targetId);
if (targetElement) {
setTimeout(() => {
targetElement.scrollIntoView(true);
window.scrollBy(0, -500);
}, 10);
}
}
});
function activateContentBasedOnHash() {
const currentHash = window.location.hash.substring(1);
if (currentHash) {
const contentElement = document.getElementById(currentHash);
if (contentElement) {
activateTabAndContent(contentElement);
}
}
}
function activateTabAndContent(contentElement) {
const tabSection = contentElement.closest('.tab-pane');
if (tabSection) {
const parentTabLink = document.querySelector(`a[href='#${tabSection.id}']`);
if (parentTabLink) {
const parentNavTabs = parentTabLink.closest('.nav-tabs');
if (parentNavTabs) {
parentNavTabs.querySelectorAll('.nav-link').forEach(link => {
link.classList.remove('active', 'show');
});
parentTabLink.classList.add('active', 'show');
}
const parentTabContent = tabSection.closest('.tab-content');
if (parentTabContent) {
parentTabContent.querySelectorAll('.tab-pane').forEach(tab => {
tab.classList.remove('active', 'show');
});
tabSection.classList.add('active', 'show');
}
}
}
}
function scrollToHashWithOffset() {
const tabHash = window.location.hash;
const targetTabLink = document.querySelector(`.nav-tabs a[href="${tabHash}"]`);
const mainContentColumn = document.querySelector('.inline');
if (targetTabLink && mainContentColumn) {
const totalHeaderHeight = getTotalHeaderHeight() + 40;
targetTabLink.scrollIntoView({behavior: 'smooth', block: 'start'});
setTimeout(() => {
mainContentColumn.scrollBy(0, -totalHeaderHeight);
}, 20);
}
}
function getTotalHeaderHeight() {
return [...document.querySelectorAll('.atd-services-header, .atd-categories-header')]
.reduce((total, header) => total + (header ? header.offsetHeight : 0), 0);
}
function handleClick(event) {
event.preventDefault();
const link = event.currentTarget;
const hash = link.getAttribute('href').substring(1);
const contentElement = document.getElementById(hash);
if (contentElement) {
activateTabAndContent(contentElement);
scrollToHashWithOffset();
}
}
function handleBackToTopClick(event) {
event.preventDefault();
const mainContentColumn = document.querySelector('.inline');
if (mainContentColumn) {
mainContentColumn.scrollTo({top: 0, behavior: 'smooth'});
}
window.scrollTo({top: 0, behavior: 'smooth'});
}
document.addEventListener('DOMContentLoaded', () => {
activateContentBasedOnHash();
scrollToHashWithOffset();
const myScrollspy = document.getElementById('myScrollspy');
if (myScrollspy) {
myScrollspy.querySelectorAll('a[href^="#"]').forEach(link => {
link.addEventListener('click', handleClick);
});
}
document.querySelectorAll('.dpfBackToTopLink, .backToTop').forEach(link => {
link.addEventListener('click', handleBackToTopClick);
});
});
// Show tab link as selected when clicked
$(document).ready(function() {
$('.nav-tabs > li > a').on('click', function(e) {
e.preventDefault();
$(this).tab('show');
$(this).addClass('active show');
});
});
// Fix for when version dropdown is on the same page as nav tab dropdown
// closes other dropdown menu to avoid overlap/usability issues
$(document).ready(function() {
// Version selector dropdown
$('.versionsButton').click(function(e) {
// Close the nav tab dropdown if open
$('.nav-link.dropdown-toggle').siblings('.dropdown-menu').removeClass('show');
});
// Nav tab selector dropdown
$('.nav-link.dropdown-toggle').click(function(e) {
// Close the version dropdown if open
$('.versionsButton').siblings('.dropdown-menu').removeClass('show');
});
});
// Handle active state management for dropdown menu in nav-tabs
// Fixes edge case issues for when version selector dropdown and
// nav-tab dropdown appear on the same page
$(document).ready(function() {
// Function to deactivate all tab panes and reset dropdown items
function deactivateAllTabs() {
$('.position .nav-item.dropdown .dropdown-menu .dropdown-item').each(function() {
const targetId = $(this).attr('href');
$(targetId).removeClass('active show');
$(this).removeClass('active show');
});
}
// Store the original pushState function
const originalPushState = history.pushState;
// Create a custom event for pushState
const pushStateEvent = new Event('pushState');
// Override pushState
history.pushState = function() {
originalPushState.apply(this, arguments);
// Dispatch the custom event
window.dispatchEvent(pushStateEvent);
};
// Add pushState listener
window.addEventListener('pushState', function() {
const params = new URLSearchParams(window.location.search);
const vValue = params.get('v');
// Handle query parameter change from version selector
if (vValue) {
// Remove active classes from all tabs
deactivateAllTabs();
$('.position').each(function() {
var firstTabPane = $(this).find('.tab-pane').first();
if (firstTabPane.length > 0) {
var tabId = firstTabPane.attr('id');
var dropdownItem = $('.dropdown-menu .dropdown-item[href="#' + tabId + '"]');
// Make first tab in each version active
dropdownItem.addClass('active show');
firstTabPane.addClass('active show');
}
});
}
});
// Handle dropdown item clicks
$('.position .nav-item.dropdown .dropdown-menu .dropdown-item').click(function(e) {
e.preventDefault();
// Remove active classes from all tabs
deactivateAllTabs();
// Add active class to clicked item
$(this).addClass('active show');
// Show corresponding tab content
const targetId = $(this).attr('href');
$(targetId).addClass('active show');
});
// Handle initial load from URL hash
if (window.location.hash) {
const tabId = window.location.hash;
if ($(tabId).length) {
// Deactivate all tabs first
deactivateAllTabs();
// Activate the correct tab and content
$(`.position .nav-item.dropdown .dropdown-menu .dropdown-item[href="${tabId}"]`).addClass('active show');
$(tabId).addClass('active show');
}
}
});
// Fix issue when anchor linking into a tab with a dropdown menu
$(document).ready(function() {
// Handle initial load from URL hash
if (window.location.hash) {
const tabId = window.location.hash;
if ($(tabId).length) {
// Deactivate active links in dropdown
$('.nav-item.dropdown .dropdown-menu .dropdown-item').each(function() {
$(this).removeClass('active show');
});
}
}
});
// Remove show class from caption elements to preserve table-caption display
// (fixes issue where the show class, which forces the display style to block,
// caused the table caption to appear inside the table, rather than above it)
$(document).ready(function() {
if (window.location.hash) {
const anchorId = window.location.hash.substring(1);
const targetElement = document.getElementById(anchorId);
if (targetElement) {
const captionParent = $(targetElement).closest('caption');
if (captionParent.length) {
captionParent.removeClass('show');
}
}
}
});