Giter Site home page Giter Site logo

app-store-server-library-node's People

Contributors

alexanderjordanbaker avatar betimer avatar chuganzy avatar coltkenn2658 avatar dependabot[bot] avatar izanger avatar rodrigoborgesdeoliveira avatar yidinghan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

app-store-server-library-node's Issues

TypeError: Right-hand side of 'instanceof' is not an object

Hey. Hey,
I'm trying to send a test notification in my API, but I'm getting this error:

TypeError: Right-hand side of 'instanceof' is not an object
    at module.exports [as sign] (index.js:13840:62)
    at AppStoreServerAPIClient2.createBearerToken (index.js:31998:29)
    at AppStoreServerAPIClient2.makeRequest (index.js:31877:45)
    at AppStoreServerAPIClient2.requestTestNotification (index.js:31989:27)
    at index.js:56986:37
    at resolveMiddleware (index.js:55711:29)
    at callRecursive (index.js:55744:31)
    at next (index.js:55753:20)
    at index.js:56961:10
    at callRecursive (index.js:55744:31) {
  stack: TypeError: Right-hand side of 'instanceof' is not …56961:10
    at callRecursive (index.js:55744:31),
  message: Right-hand side of 'instanceof' is not an object
[mf:inf] POST /trpc/apple.sendTestNotification 200 OK (29ms)

I need to load a private key into a variable because my API server does not have a file system. And according to the types signingKey should be a string.

Here is my code (all variables are fictitious):

import {
  AppStoreServerAPIClient,
  Environment,
  SendTestNotificationResponse,
} from "@apple/app-store-server-library";

import { protectedProcedure, router } from "../trpc";

const issuerId = "37a80662-515d-11ee-be56-0242ac120002";
const keyId = "LN5NHPSU2D";
const bundleId = "com.mycompany.app";
const encodedKey = `-----BEGIN PRIVATE KEY-----
fazmQVRp7bRp3Mb5EzQcVRlOWZt7uaDo8RvgHCp7r6z7tyUyAEpN1vj7gEJHiOeY
XA3kRXAZYUQaoToDEOh00N128q9FAvz2tCu47cwXVXefhp2B6gT4v7XXMd2pBldM
T9GbWO6cXs4QVFxXQM1YGGBS9TfyMqDZTQ0gbviN7u6Yb6DcP9TGHB8QSDpMxzLy
v50IEodS
-----END PRIVATE KEY-----`;
const environment = Environment.SANDBOX;

const client = new AppStoreServerAPIClient(encodedKey, keyId, issuerId, bundleId, environment);

export const appleRouter = router({
  sendTestNotification: protectedProcedure.mutation(async () => {
    try {
      const response: SendTestNotificationResponse = await client.requestTestNotification();
      console.info(response);
    } catch (e) {
      console.error(e);
    }
  }),
});

Node.js: v18.17.1

I tried changing the formatting to:

const encodedKey = `-----BEGIN PRIVATE KEY-----fazmQVRp7bRp3Mb5EzQcVRlOWZt7uaDo8RvgHCp7r6z7tyUyAEpN1vj7gEJHiOeYXA3kRXAZYUQaoToDEOh00N128q9FAvz2tCu47cwXVXefhp2B6gT4v7XXMd2pBldMT9GbWO6cXs4QVFxXQM1YGGBS9TfyMqDZTQ0gbviN7u6Yb6DcP9TGHB8QSDpMxzLyv50IEodS-----END PRIVATE KEY-----`;

const encodedKey = `fazmQVRp7bRp3Mb5EzQcVRlOWZt7uaDo8RvgHCp7r6z7tyUyAEpN1vj7gEJHiOeYXA3kRXAZYUQaoToDEOh00N128q9FAvz2tCu47cwXVXefhp2B6gT4v7XXMd2pBldMT9GbWO6cXs4QVFxXQM1YGGBS9TfyMqDZTQ0gbviN7u6Yb6DcP9TGHB8QSDpMxzLyv50IEodS`;

None of that helped.

It could be related to auth0/node-jsonwebtoken#939

TypeError Occurs When Using verifyAndDecodeNotification Method in Sandbox Environment

Hello,
I hope this message finds you well.

I am currently using version 1.4.0 of your app-store-server-library-node. However, I encountered the following error when attempting to decode a signedPayload after receiving server notifications for auto-renewable subscriptions.

스크린샷 2024-07-24 오전 10 21 22

Here are the detailed steps:
1. Initial purchase on the client side => Success
2. Processing of the initial purchase data on our own API server => Success (using the getTransactionInfo function from the same library, no errors)
3. Receive server notification for renewal payment immediately after the initial purchase => Success
4. Attempt to decode the received signedPayload by passing it to the verifyAndDecodeNotification function => Failure

As shown in the attached screenshot, the error stack trace indicates that the error originates from within the library’s internal logic, prompting me to raise this issue.

I would appreciate it if you could review this matter.

Thank you.

@apple/app-store-server-library plugin getting 401 statusCode

Getting 401 status code error when trying to call any function in app-store-server-library, Checked KeyId, issuerId, bundleId and key all the information is correct. I can call the services using postman with above keys. Here is code snipped . I am using node version v16.19.1 I also have tried with Node 18

let issuerId: string = '69a6xxxx-xxxx-....' let keyId: string = 'ABCDEFGHIJ' let bundleId: string = 'com.mycompany.app' let encodedKey: string = -----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgdxxxx-xxxx
xxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxx
xxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxx
xxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxx
mZqK4M1t
-----END PRIVATE KEY-----let client: AppStoreServerAPIClient = new AppStoreServerAPIClient(encodedKey, keyId, issuerId, bundleId, Environment.SANDBOX) let response: StatusResponse | null = null let transactionId: string = response = await client.getAllSubscriptionStatuses('2000000442013708')

verifyAndDecodeNotification throws VerificationException - cause is FetchError socket hang up

When using verifier.verifyAndDecodeNotification(signedPayload) from my local development server it throws this VerificationException when processing notification failures and I'm not sure why. I set enableOnlineChecks = true.

VerificationException Status error.status: 1
error.cause: FetchError: request to http://ocsp.apple.com/ocsp03-applerootcag3 failed, reason: socket hang up
    at ClientRequest.<anonymous> (/home/node/app/node_modules/node-fetch/lib/index.js:1501:11)
    at ClientRequest.emit (node:events:514:28)
    at Socket.socketOnEnd (node:_http_client:519:9)
    at Socket.emit (node:events:526:35)
    at endReadableNT (node:internal/streams/readable:1589:12)
    at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {
  type: 'system',
  errno: 'ECONNRESET',
  code: 'ECONNRESET'
}
error.message:
Notification failure process failure: VerificationException [Error]
    at SignedDataVerifier.verifyJWT (/home/node/app/node_modules/@apple/app-store-server-library/dist/jws_verification.js:163:23)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async SignedDataVerifier.verifyAndDecodeNotification (/home/node/app/node_modules/@apple/app-store-server-library/dist/jws_verification.js:91:28)
    at async processNotificationSignedPayload (file:///[redacted].js:64:38)
    at async file:///[redacted].js:259:24
    at async Promise.all (index 5)
    at async Module.processNotificationFailures (file:///[redacted].js:256:46)
    at async processAppStoreNotificationFailures (file:///[redacted].js:20:9) {
  status: 1,
  cause: FetchError: request to http://ocsp.apple.com/ocsp03-applerootcag3 failed, reason: socket hang up
      at ClientRequest.<anonymous> (/home/node/app/node_modules/node-fetch/lib/index.js:1501:11)
      at ClientRequest.emit (node:events:514:28)
      at Socket.socketOnEnd (node:_http_client:519:9)
      at Socket.emit (node:events:526:35)
      at endReadableNT (node:internal/streams/readable:1589:12)
      at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {
    type: 'system',
    errno: 'ECONNRESET',
    code: 'ECONNRESET'
  }
}

I'm using the same root certs that I use on my staging server in the cloud, and this error isn't thrown there. Both the dev and staging servers use Environment.SANDBOX. I'm currently preparing a production launch in a few weeks so hoping this won't manifest on the production server as well.

I can confirm after installing node-fetch that I can make requests with it, so that leads me to believe it may have something to do with the Apple root certs.

EDIT: Maybe an http:// vs. https:// issue? https://stackoverflow.com/a/70664808/544252 Though my local dev server is definitely not using self-signed https certificates or any at all.

How to best clear failed notifications from getNotificationHistory

The onlyFailures option of getNotificationHistory seems very useful to try to recover from missed notifications, but it's still a bit unclear to me how to clear a failed notification from the history if my server managed to successfully process it from the received list.

It's also a bit unclear how to ensure that I don't process a notification failure from the past in such a way that I use the data to overwrite the data from a notification that came after it, such as updating my database with auto-renewing subscription data using the failed notification where a more recent notification was successfully processed by my server with different auto-renewing subscription data.

This is all still very new to me, so my apologies if I'm somehow missing the mark here.

I get an error when trying to decrypt my signed payload

For more context it works whenever I send test notification locally

  const client = this.client();
  const response: SendTestNotificationResponse = await client.requestTestNotification();
  const checkResponse = client.getTestNotificationStatus(response.testNotificationToken);
  return checkResponse;

@apple/app-store-server-library plugin didn't working

trying to install the package into the Libray bu giving the below issue after taking so long

app-store-server-library-node-main % npm install @apple/app-store-server-library --save

npm ERR! code ENOTFOUND
npm ERR! syscall getaddrinfo
npm ERR! errno ENOTFOUND
npm ERR! network request to https://npm.apple.com/@apple%2fapp-store-server-library failed, reason: getaddrinfo ENOTFOUND npm.apple.com
npm ERR! network This is a problem related to network connectivity.
npm ERR! network In most cases you are behind a proxy or have bad network settings.
npm ERR! network
npm ERR! network If you are behind a proxy, please make sure that the
npm ERR! network 'proxy' config is set properly. See: 'npm help config'

no able to install the @apple/app-store-server-library in the Library
and event the nom install is not woking

loadRootCAs() part..

I've downloaded Apple Root CA from here https://www.apple.com/certificateauthority/

but I have no idea how I can import that file and call
const verifier = new SignedDataVerifier(appleRootCAs, enableOnlineChecks, environment, bundleId)

I'm keep getting errors like
Error: Failed to load function definition from source: Failed to generate manifest from function source: TypeError: Cannot read properties of undefined (reading 'map')

How can I load appleRootCAs and feed it to SignedDataVerifier?

"PEM routines::no start line" With "SignedDataVerifier" in Production environment

Hello everyone!

I'm validating subcription from IAP Receipt using this library.
Everything is fine during dev in Apple's "Sandbox" mode.

Once I upload my app and run it from a testFlight build I've go this error:

Error: error:0480006C:PEM routines::no start line
at new X509Certificate (node:internal/crypto/x509:119:21)
at /opt/app/node_modules/@apple/app-store-server-library/jws_verification.ts:55:65
at Array.map ()
at new SignedDataVerifier (/opt/app/node_modules/@apple/app-store-server-library/jws_verification.ts:55:53)

Here is how I initiate the call:


    const issuerId = '6f1337a3-1234-4929-9ccd-774e8c02fa18';
    const keyId = 'FG7N3NY68Y';
    const bundleId = 'com.appname.app';
    const teamId = '6JZPD4JMSJ';
    
    // appAppleId is required when the environment is Production
    const appAppleId = teamId+"."+bundleId;
    const appleEnv = Environment.PRODUCTION;

    const filePath = path.join(ROOT_FOLDER, `/keys/AppleApi_AuthKey_${keyId}.p8`);
    const privateKey = fs.readFileSync(filePath, 'utf8');

    const client = new AppStoreServerAPIClient(privateKey, keyId, issuerId, bundleId, appleEnv);

    const appleRootCAs = [
      fs.readFileSync(path.join(ROOT_FOLDER, '/keys/AppleRootCer/AppleIncRootCertificate.cer')),
      fs.readFileSync(path.join(ROOT_FOLDER, '/keys/AppleRootCer/AppleComputerRootCertificate.cer')),
      fs.readFileSync(path.join(ROOT_FOLDER, '/keys/AppleRootCer/AppleRootCA-G2.cer')),
      fs.readFileSync(path.join(ROOT_FOLDER, '/keys/AppleRootCer/AppleRootCA-G3.cer')),
    ];
    const enableOnlineChecks = true;

    console.log('privateKey = ', privateKey);

    const verifier = new SignedDataVerifier(appleRootCAs, enableOnlineChecks, appleEnv, bundleId, appAppleId);

I'm first trying to run in Production env then in sandbox as requested in apple's documentation.
The files are correctly read. Login private key shows:

privateKey = -----BEGIN PRIVATE KEY-----
MIGTAbEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg4n35kqKOEJA5ITBZ
lf5QfWlaj1re4R60jFflAmzfJQKgCgYIKoZIzj0DABehRANCAATvNxWUh/WGfw+q
OW1kMsTRNyBN7IwS861VjFvtQKN7r8wlvG1jTw+vTpsv/84xZw/H+IDmFLnOkAbS
yRfk1PcT
-----END PRIVATE KEY-----

more log values :
enableOnlineChecks : true
environment : Production
bundleId : com.appname.app
appAppleId : 6JZPD4JMSJ.com.appname.app <= is this appId format correct ?

(All keys and appname/ team id have been change for obvious security reason)

Can you see anything I do wrong ? Is Apple root Certificate loading ok ?
should we "enableOnlineChecks" ?
Tank you for your help.

Missing receiptType on decodedAppTransaction in verifyAndDecodeAppTransaction

Hello, I'm trying to verify an in app purchase was successful before unlocking digital goods on my node backend.

I'm writing a simple test function to request the transaction JWT by transactionId and then using verifyAndDecodeAppTransaction. I was getting this exception:

VerificationException [Error]
    at SignedDataVerifier.verifyAndDecodeAppTransaction (/home/jared/projects/apple-test/node_modules/@apple/app-store-server-library/dist/jws_verification.js:119:19)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async file:///home/jared/projects/apple-test/index.js:28:21 {
  status: 3,
  cause: undefined
}

Without knowing how to fix this I started adding console.logs in your library code and found that the app transaction was getting decoded properly but was missing a receiptType property that is compared against the environment string used to construct the SignedDataVerifier class.

Here is the function I'm referring to:

    async verifyAndDecodeAppTransaction(signedAppTransaction) {
        const decodedAppTransaction = await this.verifyJWT(signedAppTransaction, this.appTransactionValidator, t => t.receiptCreationDate === undefined ? new Date() : new Date(t.receiptCreationDate));
        const environment = decodedAppTransaction.receiptType;
        
        if (this.bundleId !== decodedAppTransaction.bundleId || (this.environment === Environment_1.Environment.PRODUCTION && this.appAppleId !== decodedAppTransaction.appAppleId)) {
            throw new VerificationException(VerificationStatus.INVALID_APP_IDENTIFIER);
        }
        if (this.environment !== environment) {
            throw new VerificationException(VerificationStatus.INVALID_ENVIRONMENT);
        }
        return decodedAppTransaction;
    }

Here is my decoded transaction:

{
  transactionId: '2000000542186032',
  originalTransactionId: '2000000542186032',
  bundleId: 'ai.owow.ios.owow',
  productId: 'owow.storypack.tier4',
  purchaseDate: 1709767786000,
  originalPurchaseDate: 1709767786000,
  quantity: 1,
  type: 'Consumable',
  inAppOwnershipType: 'PURCHASED',
  signedDate: 1709944116459,
  environment: 'Sandbox',
  transactionReason: 'PURCHASE',
  storefront: 'USA',
  storefrontId: '143441',
  price: 9990,
  currency: 'USD'
}

As you can see it does not have a receiptType property which causes an exception to be thrown.

Could you please advise me if I'm doing something incorrectly on my end or if this is a bug with the library?

Thank you.

applicationUsername.toLowerCase is not a function at PromotionalOfferSignatureCreator.createSignature

I'm following the documentation and try to create a signature like the following

const signatureCreator = new PromotionalOfferSignatureCreator(encodedKey, keyId, bundleId)
const signature = signatureCreator.createSignature(productId, subscriptionOfferId, signatureCreator)

but when I call my code I get an error saying applicationUsername.toLowerCase is not a function. Has anyone else run into this problem? I have a node.js server

Not compatible with Deno, Bun

Thank you for your work. However, at the moment, this library is not compatible with Deno or Bun. Are there any plans to make it compatible?

Missing an example for how to verify an IAP transaction receipt

Either we don't need to do this and we can just look up transaction IDs via this library... wr we could use an example of how to do this with this API. The docs are also unclear if you can look up transactioninfo for an unfinished transaction - for me so far, it doesn't work.

server endpoints not support ipv6?

Wondering if my test was correct, seems the Sandbox and Production endpoints are not reachable from an ipv6-only network, because the two endpoints have no DNS for ipv6. I have to use proxy to forward the requests.

> ping6 apple.com
16 bytes from 2620:149:af0::10, icmp_seq=0 hlim=58 time=174.733 ms

> ping6 api.storekit-sandbox.itunes.apple.com
ping6: getaddrinfo -- nodename nor servname provided, or not known

Thanks

API works in sandbox not production

I used the same settings, the only thing I changed is the Environment.SANDBOX to PRODUCTION.

I used the same notification url in app connect

SANDBOX works fine.

Then I got 401 from the API exception in PRODUCTION.

No way to detect fraudulent receipts.

After receiving a receipt from an app with a base64 string beginning with “MII...” as a receipt, there is no way to verify that it is a legitimate receipt.

If only the last character of a legitimate receipt is altered and POSTed to the following API, an error will occur and tampering can be detected.
However, this API has been deprecated
https://sandbox.itunes.apple.com/verifyReceipt

In the new library, the base64 of the receipt is only used to get the transactionID, which is only about 10 digits in the following code.
const transactionId = new ReceiptUtility().extractTransactionIdFromAppReceipt(receiptString)
In fact, falsifying the last character of base64 did not generate an error and returned the same transactionID.
It is noted in the comments that this method does not validate.

How can I trust the base64 string received from the app, starting with “MII...”, to assign the billing information?
If I can get the transactionID, can I trust that it is legitimate?

Error: secretOrPrivateKey must be an asymmetric key when using ES256 in Production environment

Everything works fine in the Sandbox environment. But when I switch to production, with the same private key and other variables, I got this error

error: Error: secretOrPrivateKey must be an asymmetric key when using ES256
at Object.module.exports [as sign] (/opt/app/node_modules/@apple/app-store-server-library/node_modules/jsonwebtoken/sign.js:130:22)
at AppStoreServerAPIClient.createBearerToken (/opt/app/node_modules/@apple/app-store-server-library/index.ts:365:29)
at AppStoreServerAPIClient.makeRequest (/opt/app/node_modules/@apple/app-store-server-library/index.ts:111:47)
at AppStoreServerAPIClient.getTransactionHistory (/opt/app/node_modules/@apple/app-store-server-library/index.ts:311:27)
at findLastReceiptFromOriginalTransactionId (/opt/app/v1/helpers/appleHelpers.ts:386:49)

I found that it may be caused by the wrong format of the private key file or the newline character, but it still works in the Sandbox environment. I'm using fs-extra to read the p8 file

const encodedKey = fs.readFileSync('apple-api-key.p8', { encoding: 'utf8' });

Node v16.19.0

Getting 404 error while calling methong getTransactionInfo (APIError Code: 4040010)

I am trying to fetch transaction details and constantly getting a 404 error.

When I try to fetch details for older transactions, it works.

If it might have something to do with reading the p8 file, can you explain what readFile(filePath) function does?

I am currently passing the key as
const privateKeyPath = "/SubscriptionKey.p8";
const privateKey = fs.readFileSync(__dirname + privateKeyPath, "utf8");
const serverApi2 = new AppStoreServerAPIClient( privateKey, KEY_ID, ISSUER_ID, bundleId, Environment.PRODUCTION );

Stuck on this error for quite a while now.

Exported OfferDiscountType Enum Values

Today I used patch-package to patch @apple/[email protected] for the project I'm working on.

Description: Exported OfferDiscountType enum, so that they can be referenced in a project.

Here is the diff that solved my problem:

diff --git a/node_modules/@apple/app-store-server-library/dist/index.d.ts b/node_modules/@apple/app-store-server-library/dist/index.d.ts
index 9e38534..1698782 100644
--- a/node_modules/@apple/app-store-server-library/dist/index.d.ts
+++ b/node_modules/@apple/app-store-server-library/dist/index.d.ts
@@ -47,6 +47,7 @@ export { NotificationHistoryResponse } from './models/NotificationHistoryRespons
 export { NotificationHistoryResponseItem } from './models/NotificationHistoryResponseItem';
 export { NotificationTypeV2 } from './models/NotificationTypeV2';
 export { OfferType } from './models/OfferType';
+export { OfferDiscountType } from './models/OfferDiscountType';
 export { OrderLookupResponse } from './models/OrderLookupResponse';
 export { OrderLookupStatus } from './models/OrderLookupStatus';
 export { Platform } from './models/Platform';

This issue body was partially generated by patch-package.

How can I identify the environment of the signed notification?

To create a SignedDataVerifier, I need to pass the environment.

Here's one possible approach:
First, try with the Production environment. If there is an exception and the status is INVALID_ENVIRONMENT = 3, then try with the Sandbox environment.

Is this the recommended approach?

Is available/recomended to use lib in Prod env?

First of all thanks so much for this library, the documentation plus the video is excelent to understand how it works.

I read the documentation that is not recomended to use it in Prod env because is BETA atm because of some things can be changing.

Ignoring this risk that mention, can we use it in Prod env or there is another problem for that?

If we can not be use in it yet, is there an ETA of the release? and what other npm lib recommend instead?

Sorry if this is not the media to put that suggestion and, again, thanks for that awesome lib!

High Sev CVE Flag from dependency on `jsrsasign` older than v11

Running an npm audit in a project that depends on this package turns up the following "high sev" cve flag:

jsrsasign  <11.0.0
Severity: high
Marvin Attack of RSA and RSAOAEP decryption in jsrsasign - https://github.com/advisories/GHSA-rh63-9qcf-83gf
No fix available
node_modules/jsrsasign
  @apple/app-store-server-library  *
  Depends on vulnerable versions of jsrsasign
  node_modules/@apple/app-store-server-library

While accepting that many people consider NPM CVEs to be more noise than useful information, managing them is nonetheless often a requirement of compliance to enterprise standards like SOC-2. Because of that and because of the relatively straightforward remediation pathway of updating jsrsasign to v11 or newer, I'm raising this issue to request that update be done in this package.

VerificationException status: 2(INVALID_APP_IDENTIFIER) occurs when using verifyAndDecodeNotification method in Production Environment

Hello,

I hope this message finds you well.

I am currently using version 1.4.0 of your app-store-server-library-node.
However, when trying to verify the signedPayload received from the server notification for an auto-renewable subscription using the verifyAndDecodeNotification method, a VerificationException with {status: 2 and cause: undefined} occurs.

The parameters passed to create the verifier instance have remained the same, and this error has been occurring continuously from July 27th(KST) until today. There has been no change in the app id.

In this situation, I would like to know what I should check and what I can correct.
Please provide guidance on how to resolve this issue.

Thank you.

When will this library be updated to include the additions in App Store Notification 2.9

Hi, about a week ago Apple released App Store Server Notifications v 2.9:

https://developer.apple.com/documentation/appstoreservernotifications/jwstransactiondecodedpayload?changes=latest_minor

This includes having the price and currency in the JWSTransactionDecodedPayload. I was wondering when we can expect this library to be updated to incorporate these changes. I see you already have a commit:

7db7227

but just wanted to know when you will be making a release for it.

[Question] Contributions

Hello 👋, I'm glad to see that Apple is creating this official library to make it easier to integrate with the App Store Server APIs.

I was looking for a contributions guideline or a CONTRIBUTING.md file, but couldn't find any. Is this repository open to public contributions?

getting wrong transaction id in extractTransactionIdFromAppReceipt method

This is very strange. whenever i got the transaction from client i am getting different raw receipt & after calling extractTransactionIdFromAppReceipt i am getting same transaction id every time.

        const applePayload = JSON.parse(req.body.payload);
        console.log(`😈🤨 very bad! ~ acknowledgeApplePurchase: ~ req.body.payload:`, req.body.payload);
        const receiptUtil = new ReceiptUtility();
        const transactionId = receiptUtil.extractTransactionIdFromAppReceipt(applePayload.Payload);
        console.log(`😈🤨 very bad! ~ acknowledgeApplePurchase: ~ transactionId:`, transactionId);
        if (!transactionId) return;

        const response = await appleClient.getTransactionInfo(transactionId);
        if (!response?.signedTransactionInfo) return;

        const receipt = new SignedDataVerifier(certs, true, environment, bundleId);

        const payload = await receipt.verifyAndDecodeTransaction(response.signedTransactionInfo);
        console.log(`😈🤨 very bad! ~ acknowledgeApplePurchase: ~ response.signedTransactionInfo:`, response.signedTransactionInfo);
        console.log(`😈🤨 very bad! ~ acknowledgeApplePurchase: ~ payload:`, payload);

is something wrong here??

getAllSubscriptionStatuses works in Sandbox, but not in Production

I call client.getAllSubscriptionStatuses(transactionId, status) in my Firebase function. When I set the "const environment = Environment.SANDBOX" and look up a sandbox transactionId, all works fine.

But when I set "const environment = Environment.PRODUCTION" and look up a production transactionId, I get httpStatusCode: 400, apiError: 4000006, errorMessage: 'Invalid transaction id.'.

Keep in mind my app is in production and I have transactionIds for those customers saved in my db. I am using CvdPurchase in Angular and get the transactionId once the transaction is FINISHED. Again, this works fine in Sandbox. But since I recently was approved and pushed to production, it is not working. I verified on my local as well (pointing to production). A customer reached out to me regarding the issue and this is how I noticed.

Screenshot 2024-04-19 at 10 05 20 PM

VerificationException Prod Webhook

{"severity":"ERROR","message":"webhook_apple VerificationException [Error]\n    at SignedDataVerifier.verifyAndDecodeNotification (/Users/aslam/Desktop/projects/cf/functions/node_modules/@apple/app-store-server-library/dist/jwt_verification.js:90:19)\n    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n    at async /Users/aslam/Desktop/projects/cf/functions/lib/src/app/app.js:610:42 {\n  status: 2,\n  cause: undefined\n}"}

Environment switching in production for testing

When apple testing the In-app purchases they are using a transaction from the sandbox environment,
And of course our server will use the production environment,
For the old verifyReceipt apple tells us to check if we get code 21007 so we should reach the sandbox environment,

I will note that: My app isn't reviewed yet nor did i send the in-app purchases to review, but sandbox environment works,

In the library i don't see any error that tells us to reach the sandbox environment,
So i wanted to see what i will get if i will use getTransactionInfo with a sandbox transaction and production environment,
And i got httpStatusCode: 401, should i rely on the httpStatusCode 401 as reaching the sandbox environment or this unauthorized code means something else?

Also will the library in the future handle(or already handles but i don't see it?) the environment "switching" or it should be handled by the developers?

APIException.apiError 4040010 is missing

Status 4040010, which should indicate that TRANSACTION_ID_NOT_FOUND is not returned by the service when using the production environment with a Sandbox transaction ID.
The only response is

APIException {
   httpStatusCode: 401,
    apiError: null,
    errorMessage: null
}

Getting an error in verifyAndDecodeNotification method

I am trying to verify and decode notification with verifyAndDecodeNotification method And I am getting status: 1 VERIFICATION_FAILURE,

this was the error stack

at SignedDataVerifier.verifyCertificateChain (/rater-qa/node_modules/@apple/app-store-server-library/dist/jws_verification.js:217:19)
at SignedDataVerifier.verifyJWT (/rater-qa/node_modules/@apple/app-store-server-library/dist/jws_verification.js:181:42)
at SignedDataVerifier.verifyAndDecodeNotification (/rater-qa/node_modules/@apple/app-store-server-library/dist/jws_verification.js:97:39)

TypeError: Cannot read properties of null (reading 'length') - while extracting transactionId

When I try to extract transactionId from receipt with the following :
const transactionId = receiptUtil.extractTransactionIdFromAppReceipt(base64Receipt)

I get this error :

TypeError: Cannot read properties of null (reading 'length')
    at Tp.extractTransactionIdFromAppReceipt (/node_modules/@apple/app-store-server-library/receipt_utility.ts:42:29)

My base64Receipt doesn't start with MI though like it's mentioned in docs

Original Store Kit App Receipt Server Validation

Hello,

This is similar to #106, but with more detail.

TLDR: what's the flow one should follow to validate incoming Store Kit 1 app receipts? How do we validate that the purchase effectively happened? Would it be enough to just rely on the App Store Notifications V2 with a appAccountToken?

Long description (also for SEO in case people search for some of these keywords):
I'm using the flutter package in_app_purchase, which seems to be using the original store kit. After making a purchase, I'm able to retrieve an encoded app receipt that follows this pattern: MIIUZwYJKoZIhvcNAQcCoIIUWDCCFFQCAQE...YHtBSZ/AzFBDREBwc0gprDVu7/Hd3KF9rxE3Q==

From #52 I can see you recommend the use of ReceiptUtility and extractTransactionIdFromAppReceipt, which seems to work on the encoded string from above. Then you say that we can then "call the App Store Server API to get a signed transaction", but I'm a bit confused about this process.

The docs of this project say to use the extracted transaction id for further apple store api calls, like getTransactionInfo or getTransactionHistory, correct? But how can I match the incoming encoded app receipt with the correct transaction? Isn't that the whole point?

Or is it enough to do the following and not loop the whole history?

	const client = new AppStoreServerAPIClient(
	      this._config.appleInApp.applePrivateKey,
	      this._config.appleInApp.appleKeyID,
	      this._config.appleInApp.appleIssuerID,
	      this._config.appleInApp.appleBundleID,
	      Environment.SANDBOX,
    );

    const receiptUtil = new ReceiptUtility();
    const transactionId = receiptUtil.extractTransactionIdFromAppReceipt(
      'MIIUZwYJKoZIhvcNAQcCoIIUWDCCFFQCAQE...YHtBSZ/AzFBDREBwc0gprDVu7/Hd3KF9rxE3Q==',
    );

    if (transactionId !== null) {
    	try {
    		const transactionInfo = await client.getTransactionInfo(transactionId);

    		const rootCertificates = await Promise.all([
		        fs.readFile(`${APPLE_CERTIFICATES_DIR}AppleRootCA-G2.cer`),
		        fs.readFile(`${APPLE_CERTIFICATES_DIR}AppleRootCA-G3.cer`),
		        fs.readFile(
		          `${APPLE_CERTIFICATES_DIR}AppleComputerRootCertificate.cer`,
		        ),
		        fs.readFile(`${APPLE_CERTIFICATES_DIR}AppleIncRootCertificate.cer`),
		      ]);
	
		      const verifier = new SignedDataVerifier(
		        rootCertificates,
		        true,
		        Environment.SANDBOX,
		        this._config.appleInApp.appleBundleID,
		        undefined,
		      );
		
		      await verifier.verifyAndDecodeTransaction(
		        info.signedTransactionInfo as string,
		      );
		      
		      // if it reaches this point then we consider the purchase valid, correct?
    	}
    	catch (e) {
    		// fail validation
    	}
    } else {
    	// fail validation
    }
      

I guess in general I'm just a bit confused about the whole process. This also makes me question: If we do call our server to validate the original store kit purchase, then there is no need to receive apple store notifications, right?

receiptUtil extracting transaction id `0` instead of throwing an error

When I try to extract transactionId from receipt with the following :
const transactionId = receiptUtil.extractTransactionIdFromAppReceipt(base64Receipt)

I get transactionId of 0. Frontend is sending base64 string to me in backend. Could it be the frontend is sending the wrong base64String? Or there might be something else wrong here with the library. If the bas64 is wrong, I would expect the library to throw an error instead of returning 0

TypeError: Cannot read properties of null (reading 'length') -- while extracting transactionId from receipt

When I try to extract transactionId from receipt with the following :
const transactionId = receiptUtil.extractTransactionIdFromAppReceipt(base64Receipt)

I get this error :

TypeError: Cannot read properties of null (reading 'length')
    at Tp.extractTransactionIdFromAppReceipt (/node_modules/@apple/app-store-server-library/receipt_utility.ts:42:29)

My base64Receipt doesn't start with MI though like it's mentioned in docs, it starts with TU. Frontend is sending it to me. Could it be the frontend is sending the wrong base64String? Or there might be something else wrong here with the library.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.