ios 应用商店服务器通知设置[接收应用商店服务器通知版本2]

dced5bon  于 2023-02-17  发布在  iOS
关注(0)|答案(2)|浏览(718)

我正在尝试设置我的服务器与应用程序商店通知。这样我就可以得到通知时,用户退还他们的应用程序内购买。https://developer.apple.com/documentation/appstoreservernotifications/receiving_app_store_server_notifications〈-指导线,我现在正在寻找。

The version 2 response body, responseBodyV2, contains a signedPayload that’s cryptographically signed by the App Store in JSON Web Signature (JWS) format. The JWS format increases security and enables you to decode and validate the signature on your server. The notification data contains transaction and subscription renewal information that the App Store signs in JWS. The App Store Server API and the StoreKit In-App Purchase API use the same JWS-signed format for transaction and subscription status information. For more information about JWS, see the IETF RFC 7515 specification.

根据文章,似乎我必须在一个URL中保存一个签名的有效负载代码,我与我的App Store Connect. https://gist.github.com/atpons/5279af568cb7d1b101247c02f0a022af〈共享-代码应该是这样的
所以我的问题是,
我需要做一些新的私钥,并与服务器开发人员共享?看起来像我们存储的密钥从这里https://www.apple.com/certificateauthority/,并使用它,每当我们请求?我如何获得通知?我应该只是期望expected response json structure这种通知将来到我与我的应用程序商店连接共享的URL。谢谢你阅读我的问题!

rbl8hiat

rbl8hiat1#

好吧,现在,我可能花了一天的时间在它上面,但是我终于在一堆Java和Ruby代码片段之间找到并组成了一个工作的NodeJS代码。希望其他人能从中受益。

async processAppleNotification(signedPayload: string) {
  // Here we start with importing Apple's precious Root Certificate
  // With help of NodeJS crypto's X509Certificate constructor which only works after Node15.X but to make sure I'm using Node16.17.0
  const appleRootCertificate = new X509Certificate(
    fs.readFileSync(
      path.join(__dirname, '../../../src/assets/AppleRootCAG3.cer'),
    ),
  );

  // Decode payload unsafely, we need to get header base64 values
  let decodedPayload = await this.appleJWTService.decode(signedPayload, {
    complete: true,
  });
  if (typeof decodedPayload === 'string') {
    // Just to make sure, if decode has return string
    decodedPayload = JSON.parse(decodedPayload);
  }

  const decodedHeaders = decodedPayload['header'];
  const x5cHeaders = decodedHeaders['x5c'];
  // Map all the x5c header array values, and get them as Base64 Decoded Buffer and create X509 Cert.
  const decodedX5CHeaders: X509Certificate[] = x5cHeaders.map((_header) => {
    return new X509Certificate(Buffer.from(_header, 'base64'));
  });

  // We already know the last certificate which we receive in x5c header is AppleRootCertificate
  if (!appleRootCertificate.checkIssued(
      decodedX5CHeaders[decodedX5CHeaders.length - 1],
    )) {
    throw new UnauthorizedException();
  }
  decodedX5CHeaders.push(appleRootCertificate);

  // Let's verify all the chain together, if there is any corrupted certificate
  const verificationStatuses = [];
  decodedX5CHeaders.forEach((_header, index) => {
    if (index >= decodedX5CHeaders.length - 1) {
      return;
    }
    verificationStatuses.push(
      // Verify function returns boolean
      _header.verify(decodedX5CHeaders[index + 1].publicKey),
    );
  });

  // Check the status array, if everything is okey
  if (verificationStatuses.includes(false)) {
    throw new UnauthorizedException();
  }

  // This part is a bit Critical one, I couldn't find another way to convert X509 public key to string so here we are
  // Library name is 'jwk-to-pem', for TS please use with import * as JWKPM import statement
  const publicKeyToPEM = JWKtoPem(
    decodedX5CHeaders[0].publicKey.export({
      format: 'jwk'
    }),
  );
  const verifiedPayload = await this.appleJWTService.verify(signedPayload, {
    algorithms: [decodedHeaders.alg],
    publicKey: publicKeyToPEM,
  });
  // Here we go, all validated and have the actual payload as validated
  console.log(verifiedPayload);
}
toiithl6

toiithl62#

下面是步骤:
1.获取Apple根证书:加权https://www.apple.com/certificateauthority/AppleRootCA-G3.cer
1.创建pem文件:openssl x509 -通知目录输入/苹果根CA-G3.cer输出/苹果根.pem
1.运行代码:

import { X509Certificate } from 'crypto'
import fs from 'fs'
import jwt from 'jsonwebtoken'

// parameter
const signedPayloadFile = 'path to signedPayload file, ex: /home/vannguyen/signedPayload.txt'
const appleRootPemFile = 'path to pem file in step 2, ex: /home/vannguyen/apple_root.pem'
// end

const signedPayload = fs.readFileSync(signedPayloadFile).toString()

const decodeToken = (token, segment) => {
  const tokenDecodablePart = token.split('.')[segment]
  const decoded = Buffer.from(tokenDecodablePart, 'base64').toString()
  return decoded
}

const { alg, x5c } = JSON.parse(decodeToken(signedPayload, 0))

const x5cCertificates = x5c.map(
  (header) => new X509Certificate(Buffer.from(header, 'base64'))
)
const appleRootCertificate = new X509Certificate(
  fs.readFileSync(appleRootPemFile)
)

const checkIssued = appleRootCertificate.checkIssued(
  x5cCertificates[x5cCertificates.length - 1]
)
if (!checkIssued) {
  throw new Error('Invalid token')
}

x5cCertificates.push(appleRootCertificate)

const verifierStatuses = x5cCertificates.map((x590, index) => {
  if (index >= x5cCertificates.length - 1) return true
  return x590.verify(x5cCertificates[index + 1].publicKey)
})
if (verifierStatuses.includes(false)) {
  throw new Error('Invalid token')
}
const { publicKey } = x5cCertificates[0]
const payload = JSON.parse(decodeToken(signedPayload, 1))

const transactionInfo = jwt.verify(
  payload.data.signedTransactionInfo,
  publicKey,
  {
    algorithms: alg
  }
)

console.log('transactionInfo: ', transactionInfo)

const transactionRenewalInfo = jwt.verify(
  payload.data.signedRenewalInfo,
  publicKey,
  {
    algorithms: alg
  }
)

console.log('transactionRenewalInfo: ', transactionRenewalInfo)

相关问题