# uni-app project Apple in-app payment payment usage

# Open

For more information, see Apple's official documentation In-App Purchase Item Configuration Process.

Notice

  • Only apps submitted to the App Store can activate in-app payment, and apps published with an Apple corporate account cannot be activated for use
  • According to App Store Review Guidelines Clause 3.1.1, virtual item transactions must use in-app payment , physical transactions can only use third-party payment (Alipay, WeChat, etc.)
  • The information for creating an in-app purchase project needs to be filled in completely. After saving, the status of the in-app purchase project is ready to submit. When the submitted app passes the review, the status changes to approved
  • App Store Connect users can be added on the Test Flight platform before the official launch, and this user account can be used for test payment

# Configuration

Tip: The cloud package must be submitted to take effect. Please use the custom debugging base when the real machine is running; local offline packaging reference [Apple in-app Payment Module Configuration](https://nativesupport.dcloud.net.cn/AppDocs/usemodule/iOSModuleConfig/pay?id=%e8%8b%b9%e6%9e%9c%e5%ba%94%e7%94% a8%e5%86%85%e8%b4%ad%e6%94%af%e4%bb%98)

# 5+ Apps Apple In-App Payments

# In-app payment

# Get in-app payment object

The in-app payment channel is identified as appleiap, call plus.payment.getChannels to obtain the in-app payment object:

var iap = null;  //保存应用内支付对象
plus.payment.getChannels(function(channels){
    for (var i in channels) {
        var channel = channels[i];
        // Get the channel with id 'appleiap'
        if (channel.id === 'appleiap') {
            iap = channel;
        }
    }
  }, function(e){
    console.log("获取iap支付通道失败:" + e.message);
});

# 获取商品信息

Before initiating payment, you need to call requestOrder and pass in the product ID (productId) to obtain order information:

// The item in the ids array is the in-app purchase item product ID (productId) configured by App Store Connect
var ids = ['商品ID 1', '商品ID 2'];
// iap is the in-app payment object
iap.requestOrder(ids, function(e) {  
    // Get the order information success callback method
    console.log('requestOrder success: ' + JSON.stringify(e));
  }, function(e) {
    // Callback method for failure to get order information
    console.log('requestOrder failed: ' + JSON.stringify(e));
});
# 商品信息参数说明

Object对象类型

属性 类型 说明
title String 产品标题
description String 产品描述
productid String 产品id
price Number 价格
pricelocal String 币种,例如: zh_CN@currency=CNY
discount Array 折扣信息(HBuilderX 3.7.0+ 手机系统iOS12.2+支持)
# 优惠促销信息参数说明
属性 类型 说明
price Number 促销价格
periodUnit String 周期单位(day: 日,week: 周,month: 月,year: 年)
discountType String 优惠类型(introductory: 推介促销 subscription: 订阅促销)
promotionType String 促销类型(payAsYouGo: 随用随付,payUpFront: 预先支付,freeTrial: 免费试用)
code String 促销代码
units Number 促销期数

# Initiate payment

Call plus.payment.request(channel, orderInfo, successCB, errorCB) to initiate payment, the channel parameter is in-app Payment object, the orderInfo parameter is the order object

使用订阅促销优惠参考服务端生成签名

# Order object parameter description

Object object type

Attribute Type Required Description
productid String Yes App Store Connect configured in-app purchase item product ID (productId)
username String No User ID
manualFinishTransaction Boolean 3.5.1+ 支持,手动关闭订单,值为 false 时支付完成后自动关闭订单,true时不关闭订单,需要在合适的时机调用 finishTransaction 关闭订单。建议设置为 true, 默认值为 false 是为了向下兼容
paymentDiscount Object 促销优惠(HBuilderX 3.7.0+ 手机系统iOS12.2+支持)
# 促销优惠参数说明
属性 类型 必填 说明
offerIdentifier String 促销id(客户端获取)
keyIdentifier String 密钥(appstore后台获取)
nonce String 服务器生成的UUID(必须小写 24小时有效)
signature String 签名(服务器生成)
timestamp Number 服务器创建证书的时间戳(毫秒 24小时有效)
# Sample code
// restoreFlag flag, used to determine whether the restoreComplateRequest method needs to be called when the page is displayed
var restoreFlag = true; // 调用支付接口时标记 restoreFlag = true , 实际应用请将标记存储在 storage 中  
plus.payment.request(iap, {
    productid: "商品id",
    username: "appusername", // 用户标识  
    manualFinishTransaction: true // 3.5.1+ 支持,设置此参数后需要开发者主动关闭订单,参见下面的关闭订单方法 finishTransaction()
    paymentDiscount: {
        offerIdentifier:"",
        keyIdentifier:"",
        nonce:"",
        signature:"",
        timestamp:0
    } // 3.7.0+ 支持
  }, function(result){
    restoreFlag = false; // 支付成功清除标记 restoreFlag = false  
    // The payment is successful, the result is the IAP commodity transaction information object IAPTransaction needs to pass the returned payment voucher to the backend for secondary authentication
  }, function(e){
	// Payment failed return error message
});

# Restore Purchases

function restoreComplateRequest() {
    iap.restoreComplateRequest({
		manualFinishTransaction: true // 3.5.1+ 支持,设置此参数后需要开发者主动关闭订单,参见下面的关闭订单方法 finishTransaction()
	}, function(results){
        // The results format is an array to store the recovered IAP commodity transaction information object IAPTransaction, and the returned payment voucher needs to be passed to the backend for secondary authentication
    });
}

restoreComplateRequest description:

  • Purchased non-consumable items and subscription items
  • Lost items (all types) Note: Consumption type products that have lost orders After the payment is completed, the first call to this interface can return the payment voucher

# Close order

3.5.1+ start support

function finishTransaction() {
  // IAP commodity transaction information object IAPTransaction
    iap.finishTransaction(transaction, (success) => {
      console.log('关闭订单成功');
    }, (fail) => {
      console.log('关闭订单失败');
    });
}

Precautions:

  1. This method will fail when the manualFinishTransaction parameter of restoreComplateRequest and plus.payment.request is false before calling

# Lost order detection

Call restoreComplateRequest in the listener page resume event callback

document.addEventListener('resume',function(){  
    if(需要restore的触发条件) {  
        restoreComplateRequest({
			 manualFinishTransaction: true // 3.5.1+ 支持
		});
    }  
},false); 

# Lost order problem description

The user does not bind the AppStore payment method, calls plus.payment.request to prepare for payment, triggers the fail callback, errCode=2, the user does not bind the payment method, and the in-app payment process ends. The system pop-up box guides the user to bind the payment method. This process will jump to the system application AppStore to bind the payment method. If the binding is successful, the synchronization payment is successful, and the user successfully pays.

3.5.1 +

  • Added manual close order parameter manualFinishTransaction, call iapChannel.finishTransaction at the right time to close the order

  • Added close order method iapChannel.finishTransaction(Transaction, <Function> success, <Function> fail)

// pay
plus.payment.request(iapChannel, {
  manualFinishTransaction: true
})

// recover
iapChannel.restoreComplateRequest({
  manualFinishTransaction: true
})
  • After calling plus.payment.request, the failure callback may be triggered for the following reasons
  1. Network reasons
  2. User binds the card for the first time

Call the restore purchase restoreComplateRequest after a period of time to get the last abnormal or uncompleted order

  • The correct way to close an order
  1. The client obtains the successful payment and passes it to the server
  2. Notify the client after the server requests the second confirmation from the Apple server to be valid
  3. After the second confirmation, you can safely call finishTransaction to close the order

Notice:

  • When the order is not closed, even if you uninstall the app and call the restore purchase restoreComplateRequest, you can still get it
  • The app downloaded by account A, switch account B, and call restoreComplateRequest, the system pops up prompting that the purchase fails to restore

# common problem

  • Jailbroken machines may have the risk of in-app payment and may function abnormally
  • Reference for anti-swiping on the server side IAP payment to prevent swiping
  • Binding payment methods in advance can effectively avoid lost orders, for example: plus.runtime.openURL("https://apps.apple.com/account/billing"); //Jump to AppStore to bind payment method
  • 建议在每个接口调用前后添加打点日志收集,以便快速定位问题
  • 推介促销优惠参考
  • 订阅促销优惠参考