### Java API Usage: Requesting a Test Notification Source: https://github.com/apple/app-store-server-library-java/blob/main/README.md This Java code example demonstrates how to use the App Store Server API client to request a test notification. It requires your issuer ID, key ID, bundle ID, the path to your private key file, and the environment (SANDBOX or PRODUCTION). The code handles potential API exceptions and IO errors. ```java import com.apple.itunes.storekit.client.APIException; import com.apple.itunes.storekit.client.AppStoreServerAPIClient; import com.apple.itunes.storekit.model.Environment; import com.apple.itunes.storekit.model.SendTestNotificationResponse; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; public class APIExample { public static void main(String[] args) throws Exception { String issuerId = "99b16628-15e4-4668-972b-eeff55eeff55"; String keyId = "ABCDEFGHIJ"; String bundleId = "com.example"; Path filePath = Path.of("/path/to/key/SubscriptionKey_ABCDEFGHIJ.p8"); String encodedKey = Files.readString(filePath); Environment environment = Environment.SANDBOX; AppStoreServerAPIClient client = new AppStoreServerAPIClient(encodedKey, keyId, issuerId, bundleId, environment); try { SendTestNotificationResponse response = client.requestTestNotification(); System.out.println(response); } catch (APIException | IOException e) { e.printStackTrace(); } } } ``` -------------------------------- ### Verify and Decode App Store Signed Transactions in Java Source: https://context7.com/apple/app-store-server-library-java/llms.txt This example demonstrates how to verify the signature and decode the payload of a signed transaction received from the App Store using the Java library. It requires Apple's root certificates and specifies the app's bundle ID, Apple ID (optional for sandbox), and environment. Online revocation checks can be enabled. ```java import com.apple.itunes.storekit.model.Environment; import com.apple.itunes.storekit.model.JWSTransactionDecodedPayload; import com.apple.itunes.storekit.verification.SignedDataVerifier; import com.apple.itunes.storekit.verification.VerificationException; import java.io.FileInputStream; import java.io.InputStream; import java.util.Set; public class TransactionVerification { public static void main(String[] args) { try { // Load Apple root certificates from https://www.apple.com/certificateauthority/ Set rootCAs = Set.of( new FileInputStream("/path/to/AppleRootCA-G2.cer"), new FileInputStream("/path/to/AppleRootCA-G3.cer") ); String bundleId = "com.example.app"; Long appAppleId = null; // Required for PRODUCTION, optional for SANDBOX Environment environment = Environment.SANDBOX; boolean enableOnlineChecks = true; // Enable revocation checking SignedDataVerifier verifier = new SignedDataVerifier( rootCAs, bundleId, appAppleId, environment, enableOnlineChecks ); // Verify signed transaction received from API or notification String signedTransaction = "eyJhbGciOiJFUzI1NiIsIng1YyI6..."; JWSTransactionDecodedPayload transaction = verifier.verifyAndDecodeTransaction(signedTransaction); System.out.println("Transaction ID: " + transaction.getTransactionId()); System.out.println("Original Transaction ID: " + transaction.getOriginalTransactionId()); System.out.println("Product ID: " + transaction.getProductId()); System.out.println("Purchase Date: " + transaction.getPurchaseDate()); System.out.println("Quantity: " + transaction.getQuantity()); System.out.println("Type: " + transaction.getType()); System.out.println("Environment: " + transaction.getEnvironment()); } catch (VerificationException e) { System.err.println("Verification failed: " + e.getStatus()); } catch (Exception e) { System.err.println("Error: " + e.getMessage()); } } } ``` -------------------------------- ### Get Subscription Statuses using Java App Store Server API Source: https://context7.com/apple/app-store-server-library-java/llms.txt This Java code snippet demonstrates how to retrieve all subscription statuses for a customer using the App Store Server API client. It requires authentication credentials, including issuer ID, key ID, bundle ID, and a signing key. The function fetches both active and expired subscriptions and processes the response, printing details about subscription groups and their transactions. Dependencies include the App Store Server Library for Java. ```java import com.apple.itunes.storekit.client.AppStoreServerAPIClient; import com.apple.itunes.storekit.model.Environment; import com.apple.itunes.storekit.model.Status; import com.apple.itunes.storekit.model.StatusResponse; import com.apple.itunes.storekit.model.SubscriptionGroupIdentifierItem; import java.nio.file.Files; import java.nio.file.Path; public class SubscriptionStatus { public static void main(String[] args) throws Exception { String issuerId = "99b16628-15e4-4668-972b-eeff55eeff55"; String keyId = "ABCDEFGHIJ"; String bundleId = "com.example.app"; Path keyPath = Path.of("/path/to/SubscriptionKey_ABCDEFGHIJ.p8"); String encodedKey = Files.readString(keyPath); AppStoreServerAPIClient client = new AppStoreServerAPIClient( encodedKey, keyId, issuerId, bundleId, Environment.PRODUCTION ); String transactionId = "2000000123456789"; // Get all active and expired subscriptions Status[] statusFilter = {Status.ACTIVE, Status.EXPIRED}; StatusResponse response = client.getAllSubscriptionStatuses( transactionId, statusFilter ); System.out.println("Bundle ID: " + response.getBundleId()); System.out.println("App Apple ID: " + response.getAppAppleId()); System.out.println("Environment: " + response.getEnvironment()); // Process subscription groups for (SubscriptionGroupIdentifierItem group : response.getData()) { System.out.println("\nSubscription Group: " + group.getSubscriptionGroupIdentifier()); group.getLastTransactions().forEach(lastTransaction -> { System.out.println(" Status: " + lastTransaction.getStatus()); System.out.println(" Original Transaction ID: " + lastTransaction.getOriginalTransactionId()); System.out.println(" Signed Transaction: " + lastTransaction.getSignedTransactionInfo()); System.out.println(" Signed Renewal Info: " + lastTransaction.getSignedRenewalInfo()); }); } } } ``` -------------------------------- ### Gradle Dependency for App Store Server Library Source: https://github.com/apple/app-store-server-library-java/blob/main/README.md This snippet shows how to add the App Store Server Java Library as a dependency in your Gradle project. Ensure you have Java 11 or later installed. The version specified is 4.0.0. ```gradle implementation 'com.apple.itunes.storekit:app-store-server-library:4.0.0' ``` -------------------------------- ### Get Status of Subscription Renewal Date Extensions Source: https://context7.com/apple/app-store-server-library-java/llms.txt This endpoint allows you to check the status of a mass renewal date extension request. You provide the request identifier and product ID to get the progress and results. ```APIDOC ## GET /apple-app-store/v1/extensions/extend/mass-renewal-date/status/{productId}/{requestIdentifier} ### Description Retrieves the status of a mass renewal date extension request. ### Method GET ### Endpoint /apple-app-store/v1/extensions/extend/mass-renewal-date/status/{productId}/{requestIdentifier} ### Parameters #### Path Parameters - **productId** (String) - Required - The identifier of the product for which the extension was requested. - **requestIdentifier** (String) - Required - The unique identifier for the mass extension request. #### Query Parameters None #### Request Body None ### Request Example ``` GET /apple-app-store/v1/extensions/extend/mass-renewal-date/status/com.example.premium_subscription/mass-extend-1678886400000 ``` ### Response #### Success Response (200) - **complete** (Boolean) - Indicates if the mass extension process is complete. - **succeededCount** (Integer) - The number of subscriptions that were successfully extended. - **failedCount** (Integer) - The number of subscriptions that failed to extend. #### Response Example ```json { "complete": true, "succeededCount": 1500, "failedCount": 5 } ``` ``` -------------------------------- ### Create Promotional Offer Signature (Java) Source: https://github.com/apple/app-store-server-library-java/blob/main/README.md Shows how to generate a signature for a promotional offer using the App Store Server Library. This requires a key ID, bundle ID, private key path, product ID, subscription offer ID, app account token, a nonce, and a timestamp. The output is an encoded signature string. ```java import com.apple.itunes.storekit.offers.PromotionalOfferSignatureCreator; import java.nio.file.Files; import java.nio.file.Path; import java.util.UUID; public class ExampleSignatureCreation { public static void main(String[] args) throws Exception { String keyId = "ABCDEFGHIJ"; String bundleId = "com.example"; Path filePath = Path.of("/path/to/key/SubscriptionKey_ABCDEFGHIJ.p8"); String encodedKey = Files.readString(filePath); PromotionalOfferSignatureCreator signatureCreator = new PromotionalOfferSignatureCreator(encodedKey, keyId, bundleId); String productId = ""; String subscriptionOfferId = ""; String appAccountToken = ""; UUID nonce = UUID.randomUUID(); long timestamp = System.currentTimeMillis(); String encodedSignature = signatureCreator.createSignature(productId, subscriptionOfferId, appAccountToken, nonce, timestamp); System.out.println(encodedSignature); } } ``` -------------------------------- ### Initialize App Store Server API Client in Java Source: https://context7.com/apple/app-store-server-library-java/llms.txt This snippet demonstrates how to initialize an authenticated App Store Server API client using Java. It requires loading a private key from App Store Connect, specifying issuer ID, key ID, bundle ID, and environment. The client is then used to send a test notification. ```java import com.apple.itunes.storekit.client.AppStoreServerAPIClient; import com.apple.itunes.storekit.client.APIException; import com.apple.itunes.storekit.model.Environment; import com.apple.itunes.storekit.model.SendTestNotificationResponse; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; public class ClientSetup { public static void main(String[] args) { try { // Load your private key from App Store Connect String issuerId = "99b16628-15e4-4668-972b-eeff55eeff55"; String keyId = "ABCDEFGHIJ"; String bundleId = "com.example.app"; Path keyPath = Path.of("/path/to/SubscriptionKey_ABCDEFGHIJ.p8"); String encodedKey = Files.readString(keyPath); Environment environment = Environment.SANDBOX; // Initialize the API client AppStoreServerAPIClient client = new AppStoreServerAPIClient( encodedKey, keyId, issuerId, bundleId, environment ); // Test the connection SendTestNotificationResponse response = client.requestTestNotification(); System.out.println("Test notification sent: " + response.getTestNotificationToken()); } catch (APIException e) { System.err.println("API Error: " + e.getApiError() + " - " + e.getErrorMessage()); System.err.println("HTTP Status: " + e.getHttpStatusCode()); } catch (IOException e) { System.err.println("IO Error: " + e.getMessage()); } catch (Exception e) { System.err.println("Error: " + e.getMessage()); } } } ``` -------------------------------- ### Generate Promotional Offer Signatures using Java Source: https://context7.com/apple/app-store-server-library-java/llms.txt This snippet demonstrates how to generate a signature for a promotional offer using the `PromotionalOfferSignatureCreator`. This signature can then be sent to an iOS app to allow users to redeem subscription discounts. It requires a private key, key ID, and bundle ID. ```java import com.apple.itunes.storekit.offers.PromotionalOfferSignatureCreator; import java.nio.file.Files; import java.nio.file.Path; import java.util.UUID; public class PromotionalOffers { public static void main(String[] args) throws Exception { String keyId = "ABCDEFGHIJ"; String bundleId = "com.example.app"; Path keyPath = Path.of("/path/to/SubscriptionKey_ABCDEFGHIJ.p8"); String encodedKey = Files.readString(keyPath); PromotionalOfferSignatureCreator signatureCreator = new PromotionalOfferSignatureCreator(encodedKey, keyId, bundleId); // Generate signature for promotional offer String productId = "com.example.premium_subscription"; String subscriptionOfferId = "summer_discount_2024"; String appAccountToken = "user_12345"; UUID nonce = UUID.randomUUID(); long timestamp = System.currentTimeMillis(); String signature = signatureCreator.createSignature( productId, subscriptionOfferId, appAccountToken, nonce, timestamp ); System.out.println("Promotional Offer Signature:"); System.out.println(" Product ID: " + productId); System.out.println(" Offer ID: " + subscriptionOfferId); System.out.println(" Nonce: " + nonce); System.out.println(" Timestamp: " + timestamp); System.out.println(" Signature: " + signature); System.out.println("\nSend this signature to your iOS app to redeem the offer"); } } ``` -------------------------------- ### Fetch App Store Transaction History with Pagination in Java Source: https://context7.com/apple/app-store-server-library-java/llms.txt This snippet shows how to fetch a customer's transaction history from the App Store using the Java library. It includes pagination to retrieve all transactions and supports filtering by product types and revocation status. Dependencies include the App Store Server Library and standard Java I/O. ```java import com.apple.itunes.storekit.client.AppStoreServerAPIClient; import com.apple.itunes.storekit.client.GetTransactionHistoryVersion; import com.apple.itunes.storekit.model.Environment; import com.apple.itunes.storekit.model.HistoryResponse; import com.apple.itunes.storekit.model.TransactionHistoryRequest; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; public class TransactionHistory { public static void main(String[] args) throws Exception { String issuerId = "99b16628-15e4-4668-972b-eeff55eeff55"; String keyId = "ABCDEFGHIJ"; String bundleId = "com.example.app"; Path keyPath = Path.of("/path/to/SubscriptionKey_ABCDEFGHIJ.p8"); String encodedKey = Files.readString(keyPath); AppStoreServerAPIClient client = new AppStoreServerAPIClient( encodedKey, keyId, issuerId, bundleId, Environment.SANDBOX ); // Build request with filters String transactionId = "2000000123456789"; TransactionHistoryRequest request = new TransactionHistoryRequest() .sort(TransactionHistoryRequest.Order.ASCENDING) .revoked(false) .productTypes(List.of( TransactionHistoryRequest.ProductType.AUTO_RENEWABLE, TransactionHistoryRequest.ProductType.NON_CONSUMABLE )); // Paginate through all transactions List allTransactions = new ArrayList<>(); HistoryResponse response = null; String revision = null; do { response = client.getTransactionHistory( transactionId, revision, request, GetTransactionHistoryVersion.V2 ); allTransactions.addAll(response.getSignedTransactions()); revision = response.getRevision(); System.out.println("Fetched " + response.getSignedTransactions().size() + " transactions, hasMore: " + response.getHasMore()); } while (response.getHasMore()); System.out.println("Total transactions: " + allTransactions.size()); } } ``` -------------------------------- ### Fetch Transaction History from App Receipt (Java) Source: https://github.com/apple/app-store-server-library-java/blob/main/README.md Demonstrates how to retrieve transaction history from an app receipt using the App Store Server Library. It requires an issuer ID, key ID, bundle ID, and a path to the private key. The output is a list of signed transactions. ```java import com.apple.itunes.storekit.client.AppStoreServerAPIClient; import com.apple.itunes.storekit.client.GetTransactionHistoryVersion; import com.apple.itunes.storekit.migration.ReceiptUtility; import com.apple.itunes.storekit.model.Environment; import com.apple.itunes.storekit.model.HistoryResponse; import com.apple.itunes.storekit.model.TransactionHistoryRequest; import java.nio.file.Files; import java.nio.file.Path; import java.util.LinkedList; import java.util.List; public class ExampleMigration { public static void main(String[] args) throws Exception { String issuerId = "99b16628-15e4-4668-972b-eeff55eeff55"; String keyId = "ABCDEFGHIJ"; String bundleId = "com.example"; Path filePath = Path.of("/path/to/key/SubscriptionKey_ABCDEFGHIJ.p8"); String encodedKey = Files.readString(filePath); Environment environment = Environment.SANDBOX; AppStoreServerAPIClient client = new AppStoreServerAPIClient(encodedKey, keyId, issuerId, bundleId, environment); String appReceipt = "MI..."; ReceiptUtility receiptUtil = new ReceiptUtility(); String transactionId = receiptUtil.extractTransactionIdFromAppReceipt(appReceipt); if (transactionId != null) { TransactionHistoryRequest request = new TransactionHistoryRequest() .sort(TransactionHistoryRequest.Order.ASCENDING) .revoked(false) .productTypes(List.of(TransactionHistoryRequest.ProductType.AUTO_RENEWABLE)); HistoryResponse response = null; List transactions = new LinkedList<>(); do { String revision = response != null ? response.getRevision() : null; response = client.getTransactionHistory(transactionId, revision, request, GetTransactionHistoryVersion.V2); transactions.addAll(response.getSignedTransactions()); } while (response.getHasMore()); System.out.println(transactions); } } } ``` -------------------------------- ### Retrieve Customer Refund History with Pagination Source: https://context7.com/apple/app-store-server-library-java/llms.txt This code retrieves a customer's refund history from the App Store Server API, supporting pagination to fetch all available refund records. It requires the App Store Server API client to be initialized with valid credentials and the transaction ID for which to look up refunds. The output includes the number of refunded transactions fetched in each page and a summary of all refunded transactions. ```java import com.apple.itunes.storekit.client.AppStoreServerAPIClient; import com.apple.itunes.storekit.model.Environment; import com.apple.itunes.storekit.model.RefundHistoryResponse; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; public class RefundLookup { public static void main(String[] args) throws Exception { String issuerId = "99b16628-15e4-4668-972b-eeff55eeff55"; String keyId = "ABCDEFGHIJ"; String bundleId = "com.example.app"; Path keyPath = Path.of("/path/to/SubscriptionKey_ABCDEFGHIJ.p8"); String encodedKey = Files.readString(keyPath); AppStoreServerAPIClient client = new AppStoreServerAPIClient( encodedKey, keyId, issuerId, bundleId, Environment.PRODUCTION ); String transactionId = "2000000123456789"; List allRefunds = new ArrayList<>(); RefundHistoryResponse response = null; String revision = null; // Paginate through refund history do { response = client.getRefundHistory(transactionId, revision); if (response.getSignedTransactions() != null) { allRefunds.addAll(response.getSignedTransactions()); System.out.println("Fetched " + response.getSignedTransactions().size() + " refunded transactions"); } revision = response.getRevision(); } while (response.getHasMore()); System.out.println("\nTotal refunds found: " + allRefunds.size()); // Verify and decode each refunded transaction if (!allRefunds.isEmpty()) { System.out.println("\nRefunded transactions:"); for (String signedTransaction : allRefunds) { System.out.println(" - " + signedTransaction.substring(0, 50) + "..."); } } } } ``` -------------------------------- ### Process App Store Server Notifications V2 in Java Source: https://context7.com/apple/app-store-server-library-java/llms.txt This Java class demonstrates how to initialize the SignedDataVerifier with root certificates and process incoming signed App Store Server Notification payloads. It verifies the signature, decodes the payload, and handles different notification types like subscription events, renewals, and refunds. Dependencies include the App Store Server Library for Java. ```java import com.apple.itunes.storekit.model.Environment; import com.apple.itunes.storekit.model.ResponseBodyV2DecodedPayload; import com.apple.itunes.storekit.model.NotificationTypeV2; import com.apple.itunes.storekit.model.Subtype; import com.apple.itunes.storekit.verification.SignedDataVerifier; import com.apple.itunes.storekit.verification.VerificationException; import java.io.FileInputStream; import java.io.InputStream; import java.util.Set; public class NotificationHandler { private final SignedDataVerifier verifier; public NotificationHandler() throws Exception { Set rootCAs = Set.of( new FileInputStream("/path/to/AppleRootCA-G2.cer"), new FileInputStream("/path/to/AppleRootCA-G3.cer") ); this.verifier = new SignedDataVerifier( rootCAs, "com.example.app", null, Environment.SANDBOX, true ); } public void handleNotification(String signedPayload) { try { ResponseBodyV2DecodedPayload notification = verifier.verifyAndDecodeNotification(signedPayload); NotificationTypeV2 type = notification.getNotificationType(); Subtype subtype = notification.getSubtype(); System.out.println("Notification Type: " + type); System.out.println("Subtype: " + subtype); // Process based on notification type switch (type) { case SUBSCRIBED: handleSubscribed(notification); break; case DID_RENEW: handleRenewal(notification); break; case DID_FAIL_TO_RENEW: handleRenewalFailure(notification); break; case EXPIRED: handleExpiration(notification); break; case REFUND: handleRefund(notification); break; default: System.out.println("Unhandled notification: " + type); } } catch (VerificationException e) { System.err.println("Notification verification failed: " + e.getStatus()); } } private void handleSubscribed(ResponseBodyV2DecodedPayload notification) { System.out.println("New subscription: " + notification.getData().getSignedTransactionInfo()); } private void handleRenewal(ResponseBodyV2DecodedPayload notification) { System.out.println("Subscription renewed: " + notification.getData().getSignedRenewalInfo()); } private void handleRenewalFailure(ResponseBodyV2DecodedPayload notification) { System.out.println("Renewal failed, grace period: " + notification.getSubtype()); } private void handleExpiration(ResponseBodyV2DecodedPayload notification) { System.out.println("Subscription expired"); } private void handleRefund(ResponseBodyV2DecodedPayload notification) { System.out.println("Transaction refunded"); } } ``` -------------------------------- ### Order Lookup by Order ID in Java Source: https://context7.com/apple/app-store-server-library-java/llms.txt This snippet demonstrates how to look up transaction information for a given order ID using the App Store Server Library. It requires authentication credentials, including a private key, key ID, issuer ID, and bundle ID. The function returns the order status and associated signed transactions if the order is valid. ```java import com.apple.itunes.storekit.client.AppStoreServerAPIClient; import com.apple.itunes.storekit.model.Environment; import com.apple.itunes.storekit.model.OrderLookupResponse; import com.apple.itunes.storekit.model.OrderLookupStatus; import java.nio.file.Files; import java.nio.file.Path; public class OrderLookup { public static void main(String[] args) throws Exception { String issuerId = "99b16628-15e4-4668-972b-eeff55eeff55"; String keyId = "ABCDEFGHIJ"; String bundleId = "com.example.app"; Path keyPath = Path.of("/path/to/SubscriptionKey_ABCDEFGHIJ.p8"); String encodedKey = Files.readString(keyPath); AppStoreServerAPIClient client = new AppStoreServerAPIClient( encodedKey, keyId, issuerId, bundleId, Environment.PRODUCTION ); String orderId = "GPA.1234-5678-9012-34567"; OrderLookupResponse response = client.lookUpOrderId(orderId); System.out.println("Order Lookup Results:"); System.out.println(" Status: " + response.getStatus()); if (response.getStatus() == OrderLookupStatus.VALID) { System.out.println(" Signed Transactions:"); response.getSignedTransactions().forEach(signedTxn -> { System.out.println(" - " + signedTxn.substring(0, 50) + "..."); }); } else { System.out.println(" Order not found or invalid"); } } } ``` -------------------------------- ### Maven Dependency for App Store Server Library Source: https://github.com/apple/app-store-server-library-java/blob/main/README.md This snippet demonstrates how to include the App Store Server Java Library as a dependency in your Maven project. Java 11 or higher is required. The version used here is 4.0.0. ```xml com.apple.itunes.storekit app-store-server-library 4.0.0 ``` -------------------------------- ### Mass Extend Subscription Renewals in Java Source: https://context7.com/apple/app-store-server-library-java/llms.txt Mass extends renewal dates for all active subscribers of a specific product. Requires a `MassExtendRenewalDateRequest` object specifying the product ID, extension duration, reason, and a unique request identifier. Returns the request identifier for status checking. ```java import com.apple.itunes.storekit.client.AppStoreServerAPIClient; import com.apple.itunes.storekit.model.Environment; import com.apple.itunes.storekit.model.ExtendReasonCode; import com.apple.itunes.storekit.model.MassExtendRenewalDateRequest; import com.apple.itunes.storekit.model.MassExtendRenewalDateResponse; import java.nio.file.Files; import java.nio.file.Path; public class RenewalExtension { public static void main(String[] args) throws Exception { String issuerId = "99b16628-15e4-4668-972b-eeff55eeff55"; String keyId = "ABCDEFGHIJ"; String bundleId = "com.example.app"; Path keyPath = Path.of("/path/to/SubscriptionKey_ABCDEFGHIJ.p8"); String encodedKey = Files.readString(keyPath); AppStoreServerAPIClient client = new AppStoreServerAPIClient( encodedKey, keyId, issuerId, bundleId, Environment.PRODUCTION ); // Mass extend all active subscribers for a product MassExtendRenewalDateRequest massRequest = new MassExtendRenewalDateRequest() .productId("com.example.premium_subscription") .extendByDays(7) .extendReasonCode(ExtendReasonCode.SERVICE_ISSUE_OR_OUTAGE) .requestIdentifier("mass-extend-" + System.currentTimeMillis()); MassExtendRenewalDateResponse massResponse = client.extendRenewalDateForAllActiveSubscribers(massRequest); System.out.println("Mass Extension:"); System.out.println(" Request ID: " + massResponse.getRequestIdentifier()); } } ``` -------------------------------- ### Check Subscription Renewal Extension Status in Java Source: https://context7.com/apple/app-store-server-library-java/llms.txt Checks the status of a mass subscription renewal date extension request. Requires the request identifier and the product ID. Returns the completion status, number of successful extensions, and number of failed extensions. ```java import com.apple.itunes.storekit.client.AppStoreServerAPIClient; import com.apple.itunes.storekit.model.Environment; import java.nio.file.Files; import java.nio.file.Path; public class RenewalExtension { public static void main(String[] args) throws Exception { String issuerId = "99b16628-15e4-4668-972b-eeff55eeff55"; String keyId = "ABCDEFGHIJ"; String bundleId = "com.example.app"; Path keyPath = Path.of("/path/to/SubscriptionKey_ABCDEFGHIJ.p8"); String encodedKey = Files.readString(keyPath); AppStoreServerAPIClient client = new AppStoreServerAPIClient( encodedKey, keyId, issuerId, bundleId, Environment.PRODUCTION ); // Check status of mass extension String requestId = "mass-extend-1678886400000"; // Example request ID String productId = "com.example.premium_subscription"; var statusResponse = client.getStatusOfSubscriptionRenewalDateExtensions( requestId, productId ); System.out.println("Extension Status:"); System.out.println(" Complete: " + statusResponse.getComplete()); System.out.println(" Success Count: " + statusResponse.getSucceededCount()); System.out.println(" Failed Count: " + statusResponse.getFailedCount()); } } ``` -------------------------------- ### Send Consumption Data for Consumable In-App Purchases Source: https://context7.com/apple/app-store-server-library-java/llms.txt This code demonstrates how to send consumption data for consumable in-app purchases to the App Store Server API. It requires an initialized App Store Server API client and a `ConsumptionRequest` object populated with relevant details such as transaction ID, consumption status, and delivery status. This is typically used in response to a `CONSUMPTION_REQUEST` notification. ```java import com.apple.itunes.storekit.client.AppStoreServerAPIClient; import com.apple.itunes.storekit.model.ConsumptionRequest; import com.apple.itunes.storekit.model.ConsumptionRequestReason; import com.apple.itunes.storekit.model.ConsumptionStatus; import com.apple.itunes.storekit.model.DeliveryStatus; import com.apple.itunes.storekit.model.Environment; import com.apple.itunes.storekit.model.LifetimeDollarsPurchased; import com.apple.itunes.storekit.model.LifetimeDollarsRefunded; import com.apple.itunes.storekit.model.Platform; import com.apple.itunes.storekit.model.PlayTime; import com.apple.itunes.storekit.model.UserStatus; import java.nio.file.Files; import java.nio.file.Path; public class ConsumptionHandler { public static void main(String[] args) throws Exception { String issuerId = "99b16628-15e4-4668-972b-eeff55eeff55"; String keyId = "ABCDEFGHIJ"; String bundleId = "com.example.app"; Path keyPath = Path.of("/path/to/SubscriptionKey_ABCDEFGHIJ.p8"); String encodedKey = Files.readString(keyPath); AppStoreServerAPIClient client = new AppStoreServerAPIClient( encodedKey, keyId, issuerId, bundleId, Environment.PRODUCTION ); // Transaction ID from CONSUMPTION_REQUEST notification String transactionId = "2000000123456789"; // Build consumption data ConsumptionRequest consumptionData = new ConsumptionRequest() .customerConsented(true) .consumptionStatus(ConsumptionStatus.NOT_CONSUMED) .platform(Platform.NON_APPLE) .sampleContentProvided(false) .deliveryStatus(DeliveryStatus.DID_NOT_DELIVER_DUE_TO_QUALITY_ISSUE) .appAccountToken("user_12345") .accountTenure(4) // 30-90 days .playTime(PlayTime.ONE_DAY_TO_FOUR_DAYS) .userStatus(UserStatus.ACTIVE) .lifetimeDollarsPurchased(LifetimeDollarsPurchased.ONE_THOUSAND_DOLLARS_TO_ONE_THOUSAND_NINE_HUNDRED_NINETY_NINE_DOLLARS_AND_NINETY_NINE_CENTS) .lifetimeDollarsRefunded(LifetimeDollarsRefunded.ZERO_DOLLARS); // Send consumption information client.sendConsumptionData(transactionId, consumptionData); System.out.println("Consumption data sent successfully for transaction: " + transactionId); System.out.println("Status: " + consumptionData.getConsumptionStatus()); System.out.println("Delivery: " + consumptionData.getDeliveryStatus()); } } ``` -------------------------------- ### Mass Extend Renewal Date for All Active Subscribers Source: https://context7.com/apple/app-store-server-library-java/llms.txt This endpoint allows you to extend the renewal date for all active subscribers of a specific product. This is useful for service outages or other widespread issues. ```APIDOC ## POST /apple-app-store/v1/extensions/extend/mass-renewal-date ### Description Extends the renewal date for all active subscribers of a specified product. ### Method POST ### Endpoint /apple-app-store/v1/extensions/extend/mass-renewal-date ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **productId** (String) - Required - The identifier of the product for which to extend subscriptions. - **extendByDays** (Integer) - Required - The number of days to extend the renewal date by. - **extendReasonCode** (Enum: `SERVICE_ISSUE_OR_OUTAGE`, `CUSTOMER_ISSUE`, `PRODUCT_ISSUE`, `OTHER`) - Required - The reason for extending the renewal date. - **requestIdentifier** (String) - Required - A unique identifier for this request. ### Request Example ```json { "productId": "com.example.premium_subscription", "extendByDays": 7, "extendReasonCode": "SERVICE_ISSUE_OR_OUTAGE", "requestIdentifier": "mass-extend-1678886400000" } ``` ### Response #### Success Response (200) - **requestIdentifier** (String) - The unique identifier for the mass extension request. #### Response Example ```json { "requestIdentifier": "mass-extend-1678886400000" } ``` ``` -------------------------------- ### Java Verification Usage: Verifying App Store Notifications Source: https://github.com/apple/app-store-server-library-java/blob/main/README.md This Java code snippet illustrates how to verify and decode an App Store Server Notification payload. It utilizes the SignedDataVerifier and requires a set of Apple's root certificates, your bundle ID, and the environment. The `appAppleId` must be provided for the Production environment. This method helps ensure the integrity and authenticity of received notifications. ```java import com.apple.itunes.storekit.model.Environment; import com.apple.itunes.storekit.model.ResponseBodyV2DecodedPayload; import com.apple.itunes.storekit.verification.SignedDataVerifier; import com.apple.itunes.storekit.verification.VerificationException; import java.io.FileInputStream; import java.io.InputStream; import java.util.Set; public class ExampleVerification { public static void main(String[] args) { String bundleId = "com.example"; Environment environment = Environment.SANDBOX; Set rootCAs = Set.of( new FileInputStream("/path/to/rootCA1"), new FileInputStream("/path/to/rootCA2") ); Long appAppleId = null; // appAppleId must be provided for the Production environment SignedDataVerifier signedPayloadVerifier = new SignedDataVerifier(rootCAs, bundleId, appAppleId, environment, true); String notificationPayload = "ey..."; try { ResponseBodyV2DecodedPayload payload = signedPayloadVerifier.verifyAndDecodeNotification(notificationPayload); System.out.println(payload); } catch (VerificationException e) { e.printStackTrace(); } } } ``` -------------------------------- ### Migrate App Receipts to Transaction IDs using Java Source: https://context7.com/apple/app-store-server-library-java/llms.txt This snippet shows how to extract a transaction ID from an app receipt using `ReceiptUtility` and then use that ID to fetch the complete transaction history from the App Store Server API. It handles pagination of results and demonstrates extracting transaction IDs from both app receipts and transactional receipts. Requires the App Store Server API client and a private key. ```java import com.apple.itunes.storekit.client.AppStoreServerAPIClient; import com.apple.itunes.storekit.client.GetTransactionHistoryVersion; import com.apple.itunes.storekit.migration.ReceiptUtility; import com.apple.itunes.storekit.model.Environment; import com.apple.itunes.storekit.model.HistoryResponse; import com.apple.itunes.storekit.model.TransactionHistoryRequest; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; public class ReceiptMigration { public static void main(String[] args) throws Exception { // Step 1: Extract transaction ID from app receipt String appReceipt = "MIITtgYJKoZIhvcNAQcCoIITpzCCE6MCAQExCzAJ..."; ReceiptUtility receiptUtil = new ReceiptUtility(); String transactionId = receiptUtil.extractTransactionIdFromAppReceipt(appReceipt); if (transactionId == null) { System.out.println("No in-app purchases found in receipt"); return; } System.out.println("Extracted Transaction ID: " + transactionId); // Step 2: Use transaction ID to fetch full history from App Store Server API String issuerId = "99b16628-15e4-4668-972b-eeff55eeff55"; String keyId = "ABCDEFGHIJ"; String bundleId = "com.example.app"; Path keyPath = Path.of("/path/to/SubscriptionKey_ABCDEFGHIJ.p8"); String encodedKey = Files.readString(keyPath); AppStoreServerAPIClient client = new AppStoreServerAPIClient( encodedKey, keyId, issuerId, bundleId, Environment.PRODUCTION ); TransactionHistoryRequest request = new TransactionHistoryRequest() .sort(TransactionHistoryRequest.Order.ASCENDING) .revoked(false) .productTypes(List.of(TransactionHistoryRequest.ProductType.AUTO_RENEWABLE)); // Fetch all transactions List allTransactions = new ArrayList<>(); HistoryResponse historyResponse = null; String revision = null; do { historyResponse = client.getTransactionHistory( transactionId, revision, request, GetTransactionHistoryVersion.V2 ); allTransactions.addAll(historyResponse.getSignedTransactions()); revision = historyResponse.getRevision(); } while (historyResponse.getHasMore()); System.out.println("Migrated " + allTransactions.size() + " transactions"); // Step 3: Extract transaction ID from transactional receipt (if needed) String transactionReceipt = "ewoJInNpZ25hdHVyZSIgPSAiQXBwbGVJ..."; String txnId = receiptUtil.extractTransactionIdFromTransactionReceipt( transactionReceipt ); System.out.println("Transaction Receipt ID: " + txnId); } } ``` -------------------------------- ### Extend Individual Subscription Renewal Date Source: https://context7.com/apple/app-store-server-library-java/llms.txt This endpoint allows you to extend the renewal date for a specific active subscription. You provide the original transaction ID and the details of the extension. ```APIDOC ## POST /apple-app-store/v1/extensions/extend/renewal-date ### Description Extends the renewal date for a single active subscription. ### Method POST ### Endpoint /apple-app-store/v1/extensions/extend/renewal-date ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **extendByDays** (Integer) - Required - The number of days to extend the renewal date by. - **extendReasonCode** (Enum: `SERVICE_ISSUE_OR_OUTAGE`, `CUSTOMER_ISSUE`, `PRODUCT_ISSUE`, `OTHER`) - Required - The reason for extending the renewal date. - **requestIdentifier** (String) - Required - A unique identifier for this request. ### Request Example ```json { "extendByDays": 30, "extendReasonCode": "SERVICE_ISSUE_OR_OUTAGE", "requestIdentifier": "unique-request-1678886400000" } ``` ### Response #### Success Response (200) - **success** (Boolean) - Indicates if the extension was successful. - **effectiveDate** (Integer) - The new effective end date of the subscription in milliseconds since the epoch. - **originalTransactionId** (String) - The original transaction identifier for the subscription. #### Response Example ```json { "success": true, "effectiveDate": 1700000000000, "originalTransactionId": "2000000123456789" } ``` ``` -------------------------------- ### Notification History Retrieval in Java Source: https://context7.com/apple/app-store-server-library-java/llms.txt This code snippet shows how to fetch the history of App Store Server Notifications for a specific transaction ID. It allows filtering by date range, transaction ID, and notification types, and supports pagination to retrieve all available notifications. The function returns a list of signed notification payloads. ```java import com.apple.itunes.storekit.client.AppStoreServerAPIClient; import com.apple.itunes.storekit.model.Environment; import com.apple.itunes.storekit.model.NotificationHistoryRequest; import com.apple.itunes.storekit.model.NotificationHistoryResponse; import com.apple.itunes.storekit.model.NotificationTypeV2; import com.apple.itunes.storekit.model.TransactionIdNotificationHistoryRequest; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; public class NotificationHistory { public static void main(String[] args) throws Exception { String issuerId = "99b16628-15e4-4668-972b-eeff55eeff55"; String keyId = "ABCDEFGHIJ"; String bundleId = "com.example.app"; Path keyPath = Path.of("/path/to/SubscriptionKey_ABCDEFGHIJ.p8"); String encodedKey = Files.readString(keyPath); AppStoreServerAPIClient client = new AppStoreServerAPIClient( encodedKey, keyId, issuerId, bundleId, Environment.PRODUCTION ); // Build notification history request long startDate = System.currentTimeMillis() - (7 * 24 * 60 * 60 * 1000L); // 7 days ago long endDate = System.currentTimeMillis(); NotificationHistoryRequest request = new TransactionIdNotificationHistoryRequest() .startDate(startDate) .endDate(endDate) .transactionId("2000000123456789") .onlyFailures(false) .notificationTypes(List.of( NotificationTypeV2.SUBSCRIBED, NotificationTypeV2.DID_RENEW, NotificationTypeV2.EXPIRED )); // Paginate through notification history List allNotifications = new ArrayList<>(); NotificationHistoryResponse response = null; String paginationToken = null; do { response = client.getNotificationHistory(paginationToken, request); if (response.getNotificationHistory() != null) { allNotifications.addAll( response.getNotificationHistory().stream() .map(item -> item.getSignedPayload()) .toList() ); System.out.println("Fetched " + response.getNotificationHistory().size() + " notifications"); } paginationToken = response.getPaginationToken(); } while (response.getHasMore()); System.out.println("\nTotal notifications retrieved: " + allNotifications.size()); } } ``` -------------------------------- ### Extend Subscription Renewal Date in Java Source: https://context7.com/apple/app-store-server-library-java/llms.txt Extends the renewal date for an individual active subscription. Requires the original transaction ID and a request object specifying the extension duration, reason, and a unique request identifier. Returns the success status, effective end date, and original transaction ID. ```java import com.apple.itunes.storekit.client.AppStoreServerAPIClient; import com.apple.itunes.storekit.model.Environment; import com.apple.itunes.storekit.model.ExtendReasonCode; import com.apple.itunes.storekit.model.ExtendRenewalDateRequest; import com.apple.itunes.storekit.model.ExtendRenewalDateResponse; import java.nio.file.Files; import java.nio.file.Path; public class RenewalExtension { public static void main(String[] args) throws Exception { String issuerId = "99b16628-15e4-4668-972b-eeff55eeff55"; String keyId = "ABCDEFGHIJ"; String bundleId = "com.example.app"; Path keyPath = Path.of("/path/to/SubscriptionKey_ABCDEFGHIJ.p8"); String encodedKey = Files.readString(keyPath); AppStoreServerAPIClient client = new AppStoreServerAPIClient( encodedKey, keyId, issuerId, bundleId, Environment.PRODUCTION ); // Extend individual subscription String originalTransactionId = "2000000123456789"; ExtendRenewalDateRequest individualRequest = new ExtendRenewalDateRequest() .extendByDays(30) .extendReasonCode(ExtendReasonCode.SERVICE_ISSUE_OR_OUTAGE) .requestIdentifier("unique-request-" + System.currentTimeMillis()); ExtendRenewalDateResponse individualResponse = client.extendSubscriptionRenewalDate(originalTransactionId, individualRequest); System.out.println("Individual Extension:"); System.out.println(" Success: " + individualResponse.getSuccess()); System.out.println(" Effective End Date: " + individualResponse.getEffectiveDate()); System.out.println(" Original Transaction ID: " + individualResponse.getOriginalTransactionId()); } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.