app store server notification test request with JWT responds 401

I am facing this issue.

I create curl command with jwt token like this

import jwt from "jsonwebtoken"
import { readFileSync } from "fs"
import {bid, iss, kid, p8FilePath} from "./values.js"
const currentDate = Math.floor(new Date().getTime() / 1000);
const expiryDate = currentDate + (10 * 60);

const header = {
  typ: "JWT",
  alg: "ES256",
  kid: kid,
};

const payload = {
  iss: iss,
  aud: "appstoreconnect-v1",
  iat: currentDate,
  exp: expiryDate,
  bid: bid,
};

const privateKey = readFileSync(p8FilePath);

const token = jwt.sign(payload, privateKey, {
  algorithm: "ES256",
  header,
});

// curl command
console.log(`curl -v -H 'Authorization: Bearer ${token}' -X POST "https://api.storekit-sandbox.itunes.apple.com/inApps/v1/notifications/test"`)

and curl responds always

*   Trying 17.36.202.8:443...
* Connected to api.storekit-sandbox.itunes.apple.com (17.36.202.8) port 443 (#0)
* ALPN: offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/ssl/cert.pem
*  CApath: none
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-AES256-GCM-SHA384
* ALPN: server accepted h2
* Server certificate:
*  subject: businessCategory=Private Organization; jurisdictionCountryName=US; jurisdictionStateOrProvinceName=California; serialNumber=C0806592; C=US; ST=California; L=Cupertino; O=Apple Inc.; CN=api.storekit-sandbox.itunes.apple.com
*  start date: Oct 17 19:50:08 2023 GMT
*  expire date: Oct 16 20:00:08 2024 GMT
*  subjectAltName: host "api.storekit-sandbox.itunes.apple.com" matched cert's "api.storekit-sandbox.itunes.apple.com"
*  issuer: C=US; O=Apple Inc.; CN=Apple Public EV Server ECC CA 1 - G1
*  SSL certificate verify ok.
* using HTTP/2
* h2 [:method: POST]
* h2 [:scheme: https]
* h2 [:authority: api.storekit-sandbox.itunes.apple.com]
* h2 [:path: /inApps/v1/notifications/test]
* h2 [user-agent: curl/8.1.2]
* h2 [accept: */*]
* h2 [authorization: Bearer eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ilc1QjVRNzJONUwifQ.eyJpc3MiOiI2OWE2ZGU3Yy0yMmFlLTQ3ZTMtZTA1My01YjhjN2MxMWE0ZDEiLCJhdWQiOiJhcHBzdG9yZWNvbm5lY3QtdjEiLCJpYXQiOjE2OTkyMjE2MTIsImV4cCI6MTY5OTIyMjIxMiwiYmlkIjoiY29tLm1pcmFpamEudGVzdCJ9.xLDfV6oDmx0RM6soUix7XMM-ilzV3YtSjrbGXe3ZzAj8jbEpGoFLafhPtRYEnEoSYWAY6GZmFPSzxQxO2i60MA]
* Using Stream ID: 1 (easy handle 0x7ff1d000b800)
> POST /inApps/v1/notifications/test HTTP/2
> Host: api.storekit-sandbox.itunes.apple.com
> User-Agent: curl/8.1.2
> Accept: */*
> Authorization: Bearer eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ilc1QjVRNzJONUwifQ.eyJpc3MiOiI2OWE2ZGU3Yy0yMmFlLTQ3ZTMtZTA1My01YjhjN2MxMWE0ZDEiLCJhdWQiOiJhcHBzdG9yZWNvbm5lY3QtdjEiLCJpYXQiOjE2OTkyMjE2MTIsImV4cCI6MTY5OTIyMjIxMiwiYmlkIjoiY29tLm1pcmFpamEudGVzdCJ9.xLDfV6oDmx0RM6soUix7XMM-ilzV3YtSjrbGXe3ZzAj8jbEpGoFLafhPtRYEnEoSYWAY6GZmFPSzxQxO2i60MA
> 
< HTTP/2 401 
< server: daiquiri/3.0.0
< date: Sun, 05 Nov 2023 22:07:20 GMT
< content-type: text/plain
< strict-transport-security: max-age=31536000; includeSubDomains
< x-apple-jingle-correlation-key: MDKTT2CACBWVV4F4V37D6QV6KI
< x-daiquiri-instance: daiquiri:45824002:st44p00it-hyhk15104701:7987:23RELEASE169:daiquiri-amp-commerce-clients-ext-001-st
< 
Unauthenticated

Request ID: MDKTT2CACBWVV4F4V37D6QV6KI.0.0
* Connection #0 to host api.storekit-sandbox.itunes.apple.com left intact

how can I fix this????

I would recommend using the App Store Server Library (https://github.com/apple/app-store-server-library-node) to call the App Store Server API, as this has token generation, as well as request/response parsing built-in

I also tried with app-store-server-library-node. It responds 401 too.

code

import { AppStoreServerAPIClient, Environment } from "@apple/app-store-server-library"
import { readFileSync } from "fs"
import { bid, iss, kid, p8FilePath } from "./values.js"

const filePath = p8FilePath
const signingKey = readFileSync(filePath).toString()
const keyId = kid
const issuerId = iss
const bundleId = bid
const environment = Environment.SANDBOX

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

try {
  const response = await client.requestTestNotification()
  console.log(response)
} catch (e) {
  console.error(e)
}

output

APIException [Error]
    at /Users/***/projects/app-store-notify/node_modules/@apple/app-store-server-library/dist/index.js:131:27
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async AppStoreServerAPIClient.requestTestNotification (/Users/kawashimatakayoshi/projects/app-store-notify/node_modules/@apple/app-store-server-library/dist/index.js:300:16)
    at async file:///Users/***/projects/app-store-notify/api.js:15:20 {
  httpStatusCode: 401,
  apiError: null
}

Same issue for me, but for real transaction from Sandbox...

app store server notification test request with JWT responds 401
 
 
Q