') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); GitHub - apple/app-store-server-library-node: The Node.js server library for the App Store Server API and App Store Server Notifications. · GitHub
Skip to content

Repository files navigation

Apple App Store Server Node.js Library

The Node.js server library for the App Store Server API, App Store Server Notifications, Retention Messaging API, and Advanced Commerce API. Also available in Swift, Python, and Java.

Table of Contents

  1. Installation
  2. Documentation
  3. Usage
  4. Support

Installation

Requirements

  • Node 16+

NPM/Yarn

# With NPM
npm install @apple/app-store-server-library --save
# With Yarn
yarn add @apple/app-store-server-library

Documentation

Documentation

WWDC Video

Obtaining an In-App Purchase key from App Store Connect

To use the App Store Server API or create promotional offer signatures, a signing key downloaded from App Store Connect is required. To obtain this key, you must have the Admin role. Go to Users and Access > Integrations > In-App Purchase. Here you can create and manage keys, as well as find your issuer ID. When using a key, you'll need the key ID and issuer ID as well.

Obtaining Apple Root Certificates

Download and store the root certificates found in the Apple Root Certificates section of the Apple PKI site. Provide these certificates as an array to a SignedDataVerifier to allow verifying the signed data comes from Apple.

Usage

API Usage

import{AppStoreServerAPIClient,Environment,SendTestNotificationResponse}from"@apple/app-store-server-library"constissuerId="99b16628-15e4-4668-972b-eeff55eeff55"constkeyId="ABCDEFGHIJ"constbundleId="com.example"constfilePath="/path/to/key/SubscriptionKey_ABCDEFGHIJ.p8"constencodedKey=readFile(filePath)// Specific implementation may varyconstenvironment=Environment.SANDBOXconstclient=newAppStoreServerAPIClient(encodedKey,keyId,issuerId,bundleId,environment)try{constresponse: SendTestNotificationResponse=awaitclient.requestTestNotification()console.log(response)}catch(e){console.error(e)}

Verification Usage

import{SignedDataVerifier}from"@apple/app-store-server-library"constbundleId="com.example"constappleRootCAs: Buffer[]=loadRootCAs()// Specific implementation may varyconstenableOnlineChecks=trueconstenvironment=Environment.SANDBOXconstappAppleId=undefined// appAppleId is required when the environment is Productionconstverifier=newSignedDataVerifier(appleRootCAs,enableOnlineChecks,environment,bundleId,appAppleId)constnotificationPayload="ey..."constverifiedNotification=awaitverifier.verifyAndDecodeNotification(notificationPayload)console.log(verifiedNotification)

Receipt Usage

import{AppStoreServerAPIClient,Environment,GetTransactionHistoryVersion,ReceiptUtility,Order,ProductType,HistoryResponse,TransactionHistoryRequest}from"@apple/app-store-server-library"constissuerId="99b16628-15e4-4668-972b-eeff55eeff55"constkeyId="ABCDEFGHIJ"constbundleId="com.example"constfilePath="/path/to/key/SubscriptionKey_ABCDEFGHIJ.p8"constencodedKey=readFile(filePath)// Specific implementation may varyconstenvironment=Environment.SANDBOXconstclient=newAppStoreServerAPIClient(encodedKey,keyId,issuerId,bundleId,environment)constappReceipt="MI..."constreceiptUtil=newReceiptUtility()consttransactionId=receiptUtil.extractTransactionIdFromAppReceipt(appReceipt)if(transactionId!=null){consttransactionHistoryRequest: TransactionHistoryRequest={sort: Order.ASCENDING,revoked: false,productTypes: [ProductType.AUTO_RENEWABLE]}letresponse: HistoryResponse|null=nulllettransactions: string[]=[]do{constrevisionToken=response!==null&&response.revision!==null ? response.revision : nullresponse=awaitclient.getTransactionHistory(transactionId,revisionToken,transactionHistoryRequest,GetTransactionHistoryVersion.V2)if(response.signedTransactions){transactions=transactions.concat(response.signedTransactions)}}while(response.hasMore)console.log(transactions)}

Promotional Offer Signature Creation

import{PromotionalOfferSignatureCreator}from"@apple/app-store-server-library"constkeyId="ABCDEFGHIJ"constbundleId="com.example"constfilePath="/path/to/key/SubscriptionKey_ABCDEFGHIJ.p8"constencodedKey=readFile(filePath)// Specific implementation may varyconstproductId="<product_id>"constsubscriptionOfferId="<subscription_offer_id>"constappAccountToken="<app_account_token>"constnonce="<nonce>"consttimestamp=Date.now()constsignatureCreator=newPromotionalOfferSignatureCreator(encodedKey,keyId,bundleId)constsignature=signatureCreator.createSignature(productId,subscriptionOfferId,appAccountToken,nonce,timestamp)console.log(signature)

Support

Only the latest major version of the library will receive updates, including security updates. Therefore, it is recommended to update to new major versions.

About

The Node.js server library for the App Store Server API and App Store Server Notifications.

Resources

Code of conduct

Contributing

Security policy

Stars

379 stars

Watchers

16 watching

Forks

Releases

Used by

Contributors

Languages