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": []
			// }
  1. 检测开发证书中对应的identifiers是否勾选了Apple Pay Payment Processing,并配置了Merchant IDs,传送 >如果还没创建Merchant IDS ,查看这里 传送
  2. 创建完成后,需要重新生成Profiles,同时需要在xcode中重新下载Profiles 传送
  3. xcode中,找到以下文件HBuilder-Hello/Supporting Files/Bundles/PandoraApi.bundle/feature.plist中添加以下传送
  4. 配置完成后,重新生成基座,导入到HbuilderX中则可。

iOS提交AppStore审核时:提示有其他支付并隐藏功能被拒的处理办法

https://ask.dcloud.net.cn/article/36447

发起支付、获取产品信息时报以下错误:

{"code":-100,"message":"Payment_appleiap:返回订单信息失败,https://ask.dcloud.net.cn/article/282"}
  1. 检查商品状态是否正常

  2. 检查登录的苹果账号是否是测试账号,是否是沙盒环境

  3. 手机设置里看下是否限制了应用内购

  4. 检查appstore connect 是否有协议状态为有效 https://appstoreconnect.apple.com/business

  5. 检查是否有科学上网,商品是否支持手机设置的地区

  6. 是否是通过requestProduct获取的商品列表

  7. 获取支付通道 (uni.getProvider)

  8. 通过支付通道获取产品列表 (iapChannel.requestProduct)

  9. 检查是否存在未关闭的订单 (iapChannel.restoreCompletedTransactions, 可选在合适的时机检查)

  10. 请求支付,传递产品信息 (uni.requestPayment)

  11. 客户端接收苹果返回的支付票据发送到服务器,在服务器请求苹果服务器验证支付是否有效

  12. 服务器验证票据有效后在客户端关闭订单 (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();  
}  
?>