iOS订阅内购优惠服务端签名

1,000 阅读1分钟

apple官方文档

因为apple签名优惠算法官方模糊不清,这里直接上一段可以直接签名的代码以java为例,已经验证可以通过apple的优惠内购,希望能帮到你

1、参数准备

字段名定义获取方法
appBundleID应用的包名apple后台
keyIdentifier密钥标识apple后台
productIdentifier商品Id客户端
offerIdentifier促销Id客户端
applicationUsername自定义参数客户端
nonce注意必须是小写服务端生成
timestamp时间戳毫秒服务端生成

2、组合参数

这里需要注意,苹果官方文档原文必须得加上“\u2063”的空字符按照参数顺序拼接起来

Combine the parameters into a UTF-8 string with an invisible separator ('\u2063') between them, in the order shown in the following code listing.

appBundleId + '\u2063' + keyIdentifier + '\u2063' + productIdentifier + '\u2063' + offerIdentifier + '\u2063' + appAccountToken + '\u2063' + nonce + '\u2063' + timestamp

3、开始签名

这里说下iosPrivateKey是我定义的apple密钥文件解析出来的字符这个需要跟keyIdentifier参数进行配合是一对组合需要在apple后台获取,下面提供一个简单的签名后的demo,其他自行调整

public static Map<String, String> appleSign(AppleSignDTO appleSignDTO) throws Exception {

    String timestamp = String.valueOf(System.currentTimeMillis());
    String nonce = IdUtil.randomUUID();
    
    String payload = appleSignDTO.getAppBundleID() + "\u2063" +
            appleSignDTO.getKeyIdentifier() + "\u2063" +
            appleSignDTO.getProductIdentifier() + "\u2063" +
            appleSignDTO.getOfferIdentifier() + "\u2063" +
            appleSignDTO.getApplicationUsername() + "\u2063" +
            nonce + "\u2063" +
            timestamp;

    String key = iosPrivateKey
            .replace("-----BEGIN PRIVATE KEY-----", "")
            .replace("-----END PRIVATE KEY-----", "")
            .replaceAll("\s+", "");
    
    KeyFactory keyFactory = KeyFactory.getInstance("EC");
    PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(key));
    PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
    Signature signature = Signature.getInstance("SHA256withECDSA");
    signature.initSign(privateKey);
    signature.update(payload.getBytes(StandardCharsets.UTF_8));
    byte[] sign = signature.sign();
    Map map = new HashMap();
    map.put("sign", Base64.getEncoder().encodeToString(sign));
    map.put("nonce", nonce);
    map.put("timestamp", timestamp);
    map.put("keyIdentifier", "xxxx");
    return map;
}