Skip to content

Instantly share code, notes, and snippets.

@jeiea
Created March 11, 2025 10:06
Show Gist options
  • Save jeiea/f87a1f1f231f3a6526a4ad473cd66f29 to your computer and use it in GitHub Desktop.
Save jeiea/f87a1f1f231f3a6526a4ad473cd66f29 to your computer and use it in GitHub Desktop.
For deno
// see https://www.npmjs.com/package/node-apple-receipt-verify#configoptions-object
// https://medium.com/axel-springer-tech/debugging-and-reading-apple-receipts-2e47f9793f74
import { parseArgs } from "jsr:@std/cli/parse-args";
import { config as appleReceiptVerifyConfig, validate } from "npm:node-apple-receipt-verify";
const params = {
string: ["environment"],
boolean: ["ignoreExpired", "extended", "verbose", "ignoreExpiredError", "excludeOldTransactions"],
alias: {
p: "environment",
ie: "ignoreExpired",
ee: "extended",
e: "verbose",
iee: "ignoreExpiredError",
eot: "excludeOldTransactions",
},
default: {
environment: "production,sandbox",
ignoreExpired: false,
extended: false,
verbose: true,
ignoreExpiredError: false,
excludeOldTransactions: false,
},
} as const;
const { _: [filePath], ...args } = parseArgs(Deno.args, params);
if (typeof filePath !== "string") {
console.log(
"Usage: deno run --allow-read --allow-env verify.ts <receipt file path>{ <optionalFlag>}\n",
);
console.log(
"Optional flags (see https://www.npmjs.com/package/node-apple-receipt-verify#configoptions-object):",
);
for (const key of [...params.string, ...params.boolean]) {
const alias = Object.keys(params.alias).find((k) =>
params.alias[k as keyof typeof params.alias] === key
);
console.log(` ${alias}, ${key}, default=${params.default[key]}, set=${args[key]}`);
}
console.log(
"\nPrecondition: Set APP_STORE_SHARED_SECRET environment variable with your shared secret\n",
);
Deno.exit(1);
}
const sharedSecret = Deno.env.get("APP_STORE_SHARED_SECRET");
if (!sharedSecret) {
console.error("Error: APP_STORE_SHARED_SECRET environment variable is not set");
Deno.exit(1);
}
const receiptData = await Deno.readTextFile(filePath);
const config = {
secret: sharedSecret,
...Object.fromEntries(Object.values(params.alias).map((name) => [name, args[name]])),
environment: args.environment.split(","),
};
appleReceiptVerifyConfig(config);
try {
const validatedData = await validate({ receipt: receiptData });
for (const data of validatedData) {
const expirationMs = parseInt(data.expirationDate);
data.expirationDateString = new Date(expirationMs).toLocaleString();
const purchaseMs = parseInt(data.purchaseDate);
data.purchaseDateString = new Date(purchaseMs).toLocaleString();
}
console.log(validatedData);
} catch (error) {
console.error(error);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment