agisboye / app-store-server-api

A Node.js client for the App Store Server API
MIT License
210 stars 32 forks source link

Signing and Verifying payload #29

Closed davlis-tr closed 1 year ago

davlis-tr commented 1 year ago

I want to simulate Apple notifications http call without using Apple device. I just want to trigger it on my own, using some http client, so I can be in ownership what payload of notification (decoded one) is, so it's easier to test.

In order to achieve that first of all I need to sign payload, how can I achieve that? I assume I need private key and later on I can use some library (e.g jsonwebtoken) to sign payload. I also need fingerprint and pass it to function that decodes payload (e.g to decodeTransaction). How I can get fingerprint that would match private key?

If you can help, or redirect me to some documentation that would be helpful.

Regards

agisboye commented 1 year ago

Hi,

Apple signs the payload using JWS. You should be able to create your own payloads in the same manner and sign them using your own keys. The fingerprint is simply the SHA256 digest of the cert represented using hex encoding with a colon between each byte. I reckon you can use OpenSSL in your terminal for most if not all of the tasks.

If you're just trying to test your notification handling code, you should use the requestTestNotification to request that Apple sends you a notification. It'll be much easier than having to create your own notifications.

Erfa commented 12 months ago

If you are trying to implement unit tests for your app's response to notifications like REFUND, another approach is to just mock the response from this library. Something like this:

import * as appStoreApi from "app-store-server-api"

jest.spyOn(appStoreApi, "decodeNotificationPayload")
  .mockResolvedValue({
    notificationType: "REFUND",
    data: {signedTransactionInfo: ""},
  })

jest.spyOn(appStoreApi, "isDecodedNotificationDataPayload")
  .mockReturnValue(true)

jest.spyOn(appStoreApi, "decodeTransaction")
  .mockResolvedValue({
    bundleId: "com.bundle.id",
    originalTransactionId: "1",
    productId: "some_product",
  })