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

应用Apple订阅优惠时触发SKErrorCodeDomain错误码18求助(签名生成已验证通过)

应用Apple订阅优惠时触发SKErrorCodeDomain错误码18求助(签名生成已验证通过)

大家好,我现在碰到个棘手的问题:我的APP里已经正常跑通Apple订阅功能了,但在尝试应用订阅优惠时,一直触发SKErrorCodeDomain错误码18。我的需求是只让用户首次订阅时能使用这个优惠码,折腾了好几天,搜遍各种方案都没解决,下面把我的配置和试过的操作全列出来,求各位大佬帮忙排查下哪里出问题了!

一、订阅优惠的配置详情

我在App Store Connect里的优惠设置如下:

  • 优惠类型:首月优惠
  • 用户资格:新用户、现有用户、过期订阅用户均可使用
  • 优惠码状态:已激活

优惠码创建流程:App Store Connect → 目标应用 → 订阅项目 → 选中对应订阅产品 → 优惠码 → 添加 → 自定义码

二、促销优惠签名的生成配置

我严格按照Apple的要求生成签名,Payload的构造完全遵循规范:
appBundleId + '\u2063' + keyIdentifier + '\u2063' + productIdentifier + '\u2063' + offerIdentifier + '\u2063' + appAccountToken + '\u2063' + nonce + '\u2063' + timestamp

用到的密钥和标识符都是从正确路径获取的:

  • keyIdentifier、issuerId、.p8文件:App Store Connect → 用户与访问 → 集成 → 内购相关模块
  • 测试用户:在App Store Connect → 用户与访问 → 沙箱 → 测试账户 创建,且已经用该账户登录测试用的iPhone

三、我已经尝试过的排查操作

  1. 反复核对Payload里所有字段的取值,确保每个值都完全正确
  2. 时间戳试过两种格式:秒级和毫秒级(按文档要求应该用毫秒,两种都测试过)
  3. appAccountToken尝试了三种取值:
    • 有效的UUID字符串
    • 空字符串
    • 完全不传入这个字段
  4. 直接用Apple官方的服务端签名生成示例代码来生成签名,还按文档方法本地验证了签名,结果是验证通过的
  5. 用Apple提供的工具本地校验生成的签名,显示有效

四、我的签名生成服务端代码(Node.js)

const express = require('express');
const router = express.Router();
const crypto = require('crypto');
const ECKey = require('ec-key');
const uuidv4 = require('uuid/v4');
const fs = require('fs');

function getKeyID() {
    return "KEYIDXXXXX"; // 实际使用的是正确的Key ID
}

router.post('/offer', function(req, res) {
    const appBundleID = req.body.appBundleID;
    const productIdentifier = req.body.productIdentifier;
    const subscriptionOfferID = req.body.offerID;
    const applicationUsername = req.body.applicationUsername;
    const nonce = uuidv4();
    const currentDate = new Date();
    const timestamp = currentDate.getTime();
    const keyID = getKeyID();

    // 用零宽分隔符\u2063拼接Payload
    const payload = appBundleID + '\u2063' + keyID + '\u2063' + productIdentifier + '\u2063' + subscriptionOfferID + '\u2063' + applicationUsername + '\u2063'+ nonce + '\u2063' + timestamp;

    // 读取本地.p8密钥文件
    const keyString = fs.readFileSync('./SubscriptionKey_KEYIDXXXXX.p8', 'utf8');
    const key = new ECKey(keyString, 'pem');

    // 生成签名
    const cryptoSign = key.createSign('SHA256');
    cryptoSign.update(payload);
    const signature = cryptoSign.sign('base64');

    // 本地验证签名有效性
    const verificationResult = key.createVerify('SHA256').update(payload).verify(signature, 'base64');
    console.log("Verification result: " + verificationResult) // 此处输出为true

    // 返回签名相关参数给客户端
    res.setHeader('Content-Type', 'application/json');
    res.json({
        'keyID': keyID,
        'nonce': nonce,
        'timestamp': timestamp,
        'signature': signature
    });
});

module.exports = router;

五、Postman请求信息

我用Postman调用签名接口的参数如下:

  • 请求URL:http://192.168.1.141:3004/offer
  • 请求JSON:
{
  "appBundleID":"com.app.bundleid",
  "productIdentifier":"subscription.product.id",
  "offerID":"OFFERCODE1",
  "applicationUsername":"01234b43791ea309a1c3003412bcdaaa"
}

现在能想到的点都检查了一遍,签名本地验证也通过,但客户端应用优惠时就是报错18,有没有大佬遇到过类似情况?或者有没有我漏掉的配置细节?万分感谢!

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 13:05:28