You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用PHP实现Flutter应用in_app_purchase内购订单的服务端验证

Flutter端前置处理

完成支付流程后先提取凭证上传给PHP后端,拿到核验成功结果后再解锁权益、完成交易闭环:

  • 从in_app_purchase返回的PurchaseDetails对象中提取对应平台的购买凭证:
    • 安卓(Google Play):取verificationData.serverVerificationData,为包含purchaseToken、productId、packageName字段的JSON字符串
    • iOS(App Store):取verificationData.serverVerificationData,为Base64编码的交易收据字符串
  • 将上述凭证、对应商品ID、业务侧用户唯一标识通过POST请求传入PHP后端接口
PHP后端核验逻辑

1. 安卓Google Play订单核验

先在Google Play Console后台获取服务账号JSON密钥,再通过官方API完成核验:

  • 用Composer安装依赖:composer require google/apiclient:^2.0
  • 参考核验代码:
<?php
require __DIR__ . '/vendor/autoload.php';

$client = new Google_Client();
$client->setAuthConfig('你的服务账号密钥.json本地路径');
$client->addScope('https://www.googleapis.com/auth/androidpublisher');

$androidPublisher = new Google_Service_AndroidPublisher($client);
// 取前端传入的对应参数
$packageName = '你的应用包名';
$productId = $_POST['product_id'];
$purchaseToken = $_POST['purchase_token'];

try {
    $response = $androidPublisher->purchases_products->get($packageName, $productId, $purchaseToken);
    // purchaseState为0代表已完成购买
    if ($response->getPurchaseState() === 0) {
        // 此处写入业务逻辑:记录订单到数据库、给对应用户解锁权益等
        echo json_encode(['code' => 200, 'msg' => '核验成功']);
    } else {
        echo json_encode(['code' => 400, 'msg' => '订单无效']);
    }
} catch (Exception $e) {
    echo json_encode(['code' => 500, 'msg' => '核验失败:' . $e->getMessage()]);
}
?>

2. iOS App Store订单核验

走苹果官方收据核验接口,注意区分正式和沙箱环境:

  • 参考核验代码:
<?php
$receipt = $_POST['receipt_data'];
// 正式环境核验地址,沙箱环境地址为https://sandbox.itunes.apple.com/verifyReceipt
$url = 'https://buy.itunes.apple.com/verifyReceipt';
$postData = json_encode([
    'receipt-data' => $receipt,
    'password' => 'App Store Connect生成的共享密钥' // 仅自动续期订阅类商品需要,普通商品可省略
]);

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);

// 返回status为21007说明是沙箱收据,切换沙箱地址重新请求
if (isset($result['status']) && $result['status'] == 21007) {
    $url = 'https://sandbox.itunes.apple.com/verifyReceipt';
    // 此处复用上面的请求逻辑重发请求,省略重复代码
}

if (isset($result['status']) && $result['status'] == 0) {
    // 校验商品ID匹配,防止凭证掉包
    $valid = false;
    foreach ($result['receipt']['in_app'] as $item) {
        if ($item['product_id'] == $_POST['product_id']) {
            $valid = true;
            break;
        }
    }
    if ($valid) {
        // 此处写入业务逻辑:记录订单、解锁权益等
        echo json_encode(['code' => 200, 'msg' => '核验成功']);
    } else {
        echo json_encode(['code' => 400, 'msg' => '商品不匹配']);
    }
} else {
    echo json_encode(['code' => 400, 'msg' => '收据无效']);
}
?>
注意事项
  • 必须等后端返回核验成功结果后,再在Flutter端调用completePurchase完成交易,避免出现核验失败但交易已闭环的问题
  • 本地数据库要存储已核验的订单凭证做去重判断,防止同一个凭证重复请求解锁权益
  • 生产环境可以给第三方接口请求加超时、重试逻辑,降低接口波动对用户体验的影响

内容的提问来源于stack exchange,提问作者Mahesh Mohotti

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 07:12:03