返回

iOS沙盒环境内购退款测试指南

IOS

iOS 沙盒环境下内购退款测试指南

内购是App常见盈利模式。有效处理退款事件是维护良好用户体验、防止欺诈的关键。针对iOS沙盒环境内购,如何测试退款情景,成为开发者需面对的问题。 本文将介绍如何模拟退款测试,确保应用服务端能够正确处理相关通知。

了解沙盒环境退款机制

苹果沙盒环境提供了类似真实环境的内购机制,但同时也简化了一些流程,如退款处理。沙盒环境的退款操作,不是用户直接发起的,而是通过App Store Connect进行模拟。 理解此机制是测试的第一步,后续操作都围绕在 App Store Connect 平台进行。

模拟沙盒退款步骤

模拟退款涉及多个步骤。每个步骤都不可省略,它们确保退款能在测试环境中顺利触发,你的服务端也能准确捕获信息。

  1. 在沙盒环境中进行购买: 首先,你需要使用沙盒测试账户,在App内完成一个虚拟商品购买。确保购买成功,App客户端有正确的回调。这会触发服务器端生成对应订单。此订单随后会成为测试退款的目标。
    //Objective-C示例:模拟购买商品
    SKPayment *payment = [SKPayment paymentWithProduct:product];
    [[SKPaymentQueue defaultQueue] addPayment:payment];
    
  2. 登录 App Store Connect: 前往App Store Connect网站, 使用开发者账号登录。这里是管理应用和进行测试设置的控制台。
  3. 查找测试用户交易: 导航至 “用户和访问权限”,选择 “沙盒”, 随后在“交易记录”中找到测试账户的交易记录。 这里会显示所有由该沙盒账号购买的项目。
  4. 发起退款: 选择你先前完成的交易,点击“操作”,随后选择 “退款”。 你会看到退款类型选择,沙盒环境,使用“全部退款”进行测试。 退款后你会看到该订单交易状态变成 “已退款”。
  5. 检查App内以及服务器通知: 等待应用收到SKPaymentTransactionObserver回调。 如果服务器已经配置Server to server notification ,检查服务端是否成功接收到退款通知, 注意校验通知内容,特别是 transactionId是否对应先前购买交易.

服务器端验证

接收退款通知后,服务器端需要验证数据包的签名。 使用Apple提供的公开证书进行验签。 这能确保通知的真实性和完整性。 同时需要处理好重复通知的问题, 通常通过交易Id 来检查是否已处理过该次通知。以下提供服务器端的代码示例:

// Node.js 示例
const crypto = require('crypto');
const fetch = require('node-fetch');


async function verifyReceipt(receiptData) {
    const payload = { "receipt-data": receiptData, "password" : 'Your_Shared_Secret' };
    const response = await fetch('https://sandbox.itunes.apple.com/verifyReceipt', {
    method: 'POST',
        headers: {
            'Content-Type': 'application/json'
    },
    body: JSON.stringify(payload),
  });
    const responseData = await response.json();
  // 验证 status是否为0,0代表验证成功
  if(responseData.status !== 0){
    console.error('Verification failed ', responseData);
    return null;
    }

    const transactions = responseData.receipt && responseData.receipt.in_app;
     if(transactions)
      {
        const  lastTransaction =  transactions.slice(-1).pop();
        if(lastTransaction.cancellation_date){ //检测是否存在cancellation_date
            console.log("this is a refund transaction:", lastTransaction)
            // do something, such as cancel service or goods based on this purchase.
          return lastTransaction;
      } else {
          return  lastTransaction
        }
     }


  return null
}



async function verifyNotification(signedPayload, signature) {
  const  certificate =  `-----BEGIN CERTIFICATE-----YOUR APPLE CERTIFICATE -----END CERTIFICATE-----\n`
   const publicKey = crypto.createPublicKey(certificate);

    //verify signature
   const isVerified =  crypto.verify(null, signedPayload, publicKey,  signature);

  if(!isVerified)
   {
        console.error('Verify signature failed!');
        return;
   }

     const   jsonData=  JSON.parse(signedPayload);

    if(jsonData && jsonData.notification_type==='REFUND')
   {
          //parse transaction id etc from json data.
      // Do what you have to, according business need, refund process, like disabling the service or update order status
       const transactionInfo  = jsonData.unified_receipt &&  jsonData.unified_receipt.in_app.pop();
         console.log('Refund process with data', transactionInfo);
   }
 }

重要安全提示

  1. 务必验证通知签名: 不要直接信任收到的通知数据,务必验证签名, 防止中间人攻击。
  2. 正确配置共享密钥: 请妥善管理您的共享密钥,并使用最新密钥配置App Store服务器通知,在测试过程中需要修改验证api,测试sandbox环境
  3. 日志和监控: 确保服务器端记录完整的日志,并进行监控。这对故障排除至关重要。
  4. 及时处理: 退款操作通常伴随相关服务变更,如用户访问权限。及时处理,并反馈至客户端。

注意事项

在沙盒环境中进行退款测试, 可以采用创建新的沙盒测试用户方式进行,确保不会影响真实的用户。 退款操作通常具有异步性, 需合理安排业务逻辑代码。 此过程应加入到整个应用程序的自动化测试环节,这样在发布前,能有效降低退款异常问题带来的风险。