All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
ChainVerifier.build_ocsp_request/2no longer crashes on OTP 28. The previous implementation calledapply(:"OTP-PUB-KEY", :encode, [:OCSPRequest, ocsp_request]), but OTP 28 split the public-key ASN.1 modules and removed:"OTP-PUB-KEY". The encoder now goes through:public_key.der_encode(:OCSPRequest, ocsp_request), which dispatches correctly on OTP 27 and OTP 28. Only the online OCSP path (perform_online_checks: true) was affected; chains verified without online checks were unaffected.- Corrected the rationale comment on
verify_certificate_chain/3: the manual chain walker exists because:public_key.pkix_path_validation/3validates against wall-clock time and has no option to supply a custom timestamp. In offline mode (enable_online_checks: false),SignedDataVerifier.get_effective_date/2uses the payload'ssignedDateso historical receipts can still be processed after their signing certs expire — that path needs a custom timestamp. The previous comment incorrectly attributed the workaround to:invalid_key_usagereports on leaf certificates.
- String enum fields (
notification_type,subtype,environment,type,in_app_ownership_type,transaction_reason,offer_discount_type,revocation_type,consumption_request_reason,receipt_type,original_platform) in JWS-decoded payloads are now converted from raw strings to atoms via their enum modules. Previously these fields remained as raw strings (e.g.,"TEST"instead of:test) despite their type specs declaring atom types. raw_fields for string enums (raw_notification_type,raw_subtype,raw_environment, etc.) are now populated with the original string value in JWS-decoded payloads. Previously these were alwaysnil.keys_to_atoms/1no longer performs value conversion forenvironmentandreceipt_typefields. Value conversion is now handled consistently by modelnew/1functions viaValidator.optional_string_enum/4.
Validator.optional_string_enum/4for validating and converting string enum fields in a single step, mirroring the existingoptional_integer_enum/4for integer enums.
- Integer enum fields (
offer_type,revocation_reason,expiration_intent,auto_renew_status,price_increase_status,status) are now converted from raw integers to atoms via their enum modules. Previously these fields remained as raw integers despite their type specs declaring atom types.
Validator.optional_integer_enum/4for validating and converting integer enum fields in a single step.
- Nested structs in API responses now correctly deserialize with snake_case keys. Previously, only top-level fields were converted from camelCase, leaving nested objects like
StatusResponse.data,SubscriptionGroupIdentifierItem.last_transactions, andNotificationHistoryResponseItem.send_attemptswith unconverted camelCase string keys. LastTransactionsItem.statusnow correctly converts toStatus.t()atoms (:active,:expired, etc.) instead of incorrectly usingOrderLookupStatus.- Fixed compile-time warning for
:"OTP-PUB-KEY".encode/2in chain verifier.
__nested_fields__/0callback on response structs for declaring nested type information, enabling automatic recursive deserialization.
- Full support for App Store Server API 1.19 and App Store Server Notifications 2.0.
- New notification types:
metadata_update,migration,price_change,rescind_consent. - Advanced Commerce models for subscription management.
AppDatamodel forRESCIND_CONSENTnotifications.- Configurable JWT expiration via
jwt_expirationoption inAppStoreServerAPIClient.new/1. - PNG validation for
upload_image/3. - OCSP requester hook (
config :app_store_server_library, :ocsp_requester).
- Breaking:
AppStoreServerAPIClient.new/1andSignedDataVerifier.new/1now return{:ok, client | verifier} | {:error, reason}tuples instead of raising. - Breaking:
send_consumption_data/3renamed tosend_consumption_information/3(uses V2 API). - Breaking:
ConsumptionRequestsimplified—now requirescustomer_consented,delivery_status,sample_content_provided. - Breaking:
Subtyperenamed toSubtypeV2. - Enums use new
defenummacro with forward compatibility for unknown values.
- Deprecated consumption models:
AccountTenure,ConsumptionStatus,LifetimeDollarsPurchased,LifetimeDollarsRefunded,Platform,PlayTime,UserStatus.
Initial release.