https://uniapp.dcloud.net.cn/api/plugins/payment.html#iap
调用uni.getProvider
时返回providers
为空时,参考下面流程:
uni.getProvider({
service: 'payment',
success: (res) => {
// {
// "errMsg": "getProvider:ok",
// "service": "payment",
// "provider": [],
// "providers": []
// }
- 检测开发证书中对应的
identifiers
是否勾选了Apple Pay Payment Processing
,并配置了Merchant IDs
,传送 >如果还没创建Merchant IDS
,查看这里 传送 - 创建完成后,需要重新生成
Profiles
,同时需要在xcode
中重新下载Profiles
传送 - 在
xcode
中,找到以下文件HBuilder-Hello/Supporting Files/Bundles/PandoraApi.bundle/feature.plist
中添加以下传送 - 配置完成后,重新生成基座,导入到HbuilderX中则可。
iOS提交AppStore审核时:提示有其他支付并隐藏功能被拒的处理办法
https://ask.dcloud.net.cn/article/36447
发起支付、获取产品信息时报以下错误:
{"code":-100,"message":"Payment_appleiap:返回订单信息失败,https://ask.dcloud.net.cn/article/282"}
检查商品状态是否正常
检查登录的苹果账号是否是测试账号,是否是沙盒环境
手机设置里看下是否限制了应用内购
检查appstore connect 是否有协议状态为有效 https://appstoreconnect.apple.com/business
检查是否有科学上网,商品是否支持手机设置的地区
是否是通过requestProduct获取的商品列表
获取支付通道 (uni.getProvider)
通过支付通道获取产品列表 (iapChannel.requestProduct)
检查是否存在未关闭的订单 (iapChannel.restoreCompletedTransactions, 可选在合适的时机检查)
请求支付,传递产品信息 (uni.requestPayment)
客户端接收苹果返回的支付票据发送到服务器,在服务器请求苹果服务器验证支付是否有效
服务器验证票据有效后在客户端关闭订单 (iapChannel.finishTransaction)
uni.getProvider({
service: 'payment',
success: (res) => {
console.log('payment:',res)
const iapChannel = res.providers.find((channel) => {
return (channel.id === 'appleiap')
})
if (iapChannel) {
iapChannel.requestProduct(['com.example.cny6'], (res) => {
console.log('getProduct.res:',res)
uni.requestPayment({
provider: 'appleiap',
orderInfo: {
productid:'com.example.cny6',
username:'MW100001',
quantity:1,
},
success: (e) => {
// e 类型为 Transaction, 详见下面的描述
// {
// "payment": {
// "productid": "demo_product1",
// "quantity": "1",
// "username": "C1000348"
// },
// "transactionDate": "2024-11-18 10:14:14",
// "transactionIdentifier": "2000000777695727",
// "transactionReceipt": "xxx",
// "transactionState": "1",
// "errMsg": "requestPayment:ok"
// }
}
})
}, (err) => {
console.log('getProduct.err:',err)
})
}
}
});
校验结果状态码如下:
0 正常
21000 App Store不能读取你提供的JSON对象
21002 receipt-data 域的数据有问题
21003 receipt无法通过验证
21004 提供的shared secret不匹配你账号中的shared secret
21005 receipt服务器当前不可用
21006 receipt合法,但是订阅已过期。服务器接收到这个状态码时,receipt数据仍然会解码并一起发送
21007 receipt是Sandbox receipt,但却发送至生产系统的验证服务
21008 receipt是生产receipt,但却发送至Sandbox环境的验证服务
https://www.cnblogs.com/goloving/p/14352125.html
POST https://sandbox.itunes.apple.com/verifyReceipt [测试] POST https://buy.itunes.apple.com/verifyReceipt [正式]
Content-Type: application/json
{
"receipt-data": "xxx"
}
响应数据
{
"receipt": {
"receipt_type": "ProductionSandbox",
"adam_id": 0,
"app_item_id": 0,
"bundle_id": "com.cihai520.mxsd",
"application_version": "110",
"download_id": 0,
"version_external_identifier": 0,
"receipt_creation_date": "2024-11-18 02:14:14 Etc/GMT",
"receipt_creation_date_ms": "1731896054000",
"receipt_creation_date_pst": "2024-11-17 18:14:14 America/Los_Angeles",
"request_date": "2024-11-18 02:29:57 Etc/GMT",
"request_date_ms": "1731896997671",
"request_date_pst": "2024-11-17 18:29:57 America/Los_Angeles",
"original_purchase_date": "2013-08-01 07:00:00 Etc/GMT",
"original_purchase_date_ms": "1375340400000",
"original_purchase_date_pst": "2013-08-01 00:00:00 America/Los_Angeles",
"original_application_version": "1.0",
"in_app": [
{
"quantity": "1",
"product_id": "com.cihai520.mxsd",
"transaction_id": "2000000777695727",
"original_transaction_id": "2000000777695727",
"purchase_date": "2024-11-18 02:14:14 Etc/GMT",
"purchase_date_ms": "1731896054000",
"purchase_date_pst": "2024-11-17 18:14:14 America/Los_Angeles",
"original_purchase_date": "2024-11-18 02:14:14 Etc/GMT",
"original_purchase_date_ms": "1731896054000",
"original_purchase_date_pst": "2024-11-17 18:14:14 America/Los_Angeles",
"is_trial_period": "false",
"in_app_ownership_type": "PURCHASED"
}
]
},
"environment": "Sandbox",
"status": 0
}
提审期间,如果是正式环境:
// 如果是沙盒数据 则验证沙盒模式
if ($result['status'] == '21007') {
// 请求验证
$url = $config['url_sandbox']; //沙盒购买地址
$result = curl_post($url, $data);
$result = json_decode($result, true);
}
上面方法已经弃用,新方法:https://blog.csdn.net/Freud666/article/details/136037485
V2 整合(PHP版) > https://www.jianshu.com/p/3a25309897ee
<?php
function base64UrlDecode($data) {
return base64_decode(strtr($data, '-_', '+/'));
}
function extractPublicKeyFromX5c($x5c) {
if (empty($x5c) || !is_array($x5c)) {
throw new Exception("Invalid x5c certificate chain");
}
// 提取第一个证书(用于验证签名)
$cert = "-----BEGIN CERTIFICATE-----\n" . chunk_split($x5c[0], 64, "\n") . "-----END CERTIFICATE-----";
$publicKey = openssl_pkey_get_public($cert);
if (!$publicKey) {
throw new Exception("Failed to extract public key from x5c");
}
// --- 验证证书链是否正确 ---
$rootDerCert = file_get_contents("https://www.apple.com/certificateauthority/AppleRootCA-G3.cer");
$rootCert = base64_encode($rootDerCert);
if ($x5c[2] !== $rootCert) {
throw new Exception("Invalid certificate chain");
}
// --- 验证证书链是否正确 ---
return $publicKey;
}
function convertEs256SignatureToDer($signature) {
if (strlen($signature) !== 64) {
throw new Exception("Invalid ES256 signature length");
}
// 分别提取 r 和 s
$r = substr($signature, 0, 32);
$s = substr($signature, 32, 32);
// 移除 r 和 s 的前导零
$r = ltrim($r, "\x00");
$s = ltrim($s, "\x00");
// 如果 r 或 s 的首字节大于 0x7f,则需要添加一个前导零(防止被解释为负数)
if (ord($r[0]) > 0x7f) $r = "\x00" . $r;
if (ord($s[0]) > 0x7f) $s = "\x00" . $s;
// 构建 DER 编码的签名
$rLen = strlen($r);
$sLen = strlen($s);
$der = "\x30" . chr($rLen + $sLen + 4) . "\x02" . chr($rLen) . $r . "\x02" . chr($sLen) . $s;
return $der;
}
function jwtDecodeES256($jwt) {
// 将 JWT 按点号分隔为三部分
$parts = explode('.', $jwt);
if (count($parts) !== 3) {
throw new Exception("Invalid JWT format");
}
[$header64, $payload64, $signature64] = $parts;
// 解码 Header 和 Payload
$header = json_decode(base64UrlDecode($header64), true);
$payload = json_decode(base64UrlDecode($payload64), true);
if (!$header || !$payload) {
throw new Exception("Invalid JWT payload");
}
// 检查算法是否为 ES256
if ($header['alg'] !== 'ES256') {
throw new Exception("Unsupported algorithm: " . $header['alg']);
}
// 提取 x5c 证书链并获取公钥
if (!isset($header['x5c'])) {
throw new Exception("Missing x5c in JWT header");
}
$publicKey = extractPublicKeyFromX5c($header['x5c']);
// 解码签名
$signature = base64UrlDecode($signature64);
$derSignature = convertEs256SignatureToDer($signature);
$data = "$header64.$payload64";
// 使用 ECDSA 验证签名
$verified = openssl_verify($data, $derSignature, $publicKey, OPENSSL_ALGO_SHA256);
if ($verified !== 1) {
throw new Exception("Invalid signature".openssl_error_string());
}
// 释放公钥资源
openssl_free_key($publicKey);
return $payload;
}
// 示例用法
$jwt = "你的JWT字符串";
try {
$decodedPayload = jwtDecodeES256($jwt);
print_r($decodedPayload);
$transactionInfo = jwtDecodeES256($decodedPayload['data']['signedTransactionInfo']);
print_r($transactionInfo);
} catch (Exception $e) {
echo "解码失败: " . $e->getMessage();
}
?>