diff --git a/src/main/kotlin/com/dietmap/yaak/api/appstore/subscription/StatusUpdateNotification.kt b/src/main/kotlin/com/dietmap/yaak/api/appstore/subscription/StatusUpdateNotification.kt index a3f19f2..8508594 100644 --- a/src/main/kotlin/com/dietmap/yaak/api/appstore/subscription/StatusUpdateNotification.kt +++ b/src/main/kotlin/com/dietmap/yaak/api/appstore/subscription/StatusUpdateNotification.kt @@ -8,7 +8,7 @@ import java.io.Serializable data class StatusUpdateNotification( @get:JsonProperty("environment") val environment: String?, - @get:JsonProperty("notification_type") val notificationType: AppStoreNotificationType, + @get:JsonProperty("notification_type") val notificationType: String?, @get:JsonProperty("latest_receipt") val latestReceipt: String?, @get:JsonProperty("latest_receipt_info") val latestReceiptInfo: LatestReceiptInfo, @get:JsonProperty("expiration_intent") val expirationIntent: String?, @@ -80,7 +80,34 @@ enum class AppStoreNotificationType(private val code: Int) { /** * User has entered a price increase flow */ - PRICE_INCREASE_CONSENT(8); + PRICE_INCREASE_CONSENT(8), + + /** + * Indicates that the customer initiated a refund request for a consumable in-app purchase, + * and the App Store is requesting that you provide consumption data + */ + CONSUMPTION_REQUEST(9), + + /** + * Indicates that a customer’s subscription has successfully auto-renewed for a new transaction period. + * Provide the customer with access to the subscription’s content or service. + */ + DID_RENEW(10), + + /** + * Indicates that the App Store successfully refunded a transaction for a consumable in-app purchase, + * a non-consumable in-app purchase, or a non-renewing subscription. The cancellation_date_ms contains + * the timestamp of the refunded transaction. The original_transaction_id and product_id identify + * the original transaction and product. The cancellation_reason contains the reason. + */ + REFUND(11), + + /** + * Indicates that an in-app purchase the user was entitled to through Family Sharing is no longer + * available through sharing. StoreKit sends this notification when a purchaser disabled Family Sharing + * for a product, the purchaser (or family member) left the family group, or the purchaser asked for and received a refund. + */ + REVOKE(12); companion object { private val codes = values().associateBy(AppStoreNotificationType::code) diff --git a/src/main/kotlin/com/dietmap/yaak/domain/appstore/AppStoreSubscriptionService.kt b/src/main/kotlin/com/dietmap/yaak/domain/appstore/AppStoreSubscriptionService.kt index 72a439e..e406957 100644 --- a/src/main/kotlin/com/dietmap/yaak/domain/appstore/AppStoreSubscriptionService.kt +++ b/src/main/kotlin/com/dietmap/yaak/domain/appstore/AppStoreSubscriptionService.kt @@ -101,7 +101,7 @@ class AppStoreSubscriptionService(private val userAppClient: UserAppClient, priv var notificationType = NotificationType.SUBSCRIPTION_PURCHASED val latestReceiptInfo = statusUpdateNotification.latestReceiptInfo - when (statusUpdateNotification.notificationType) { + when (val appStoreNotificationType = parseAppStoreNotificationTypeEnum(statusUpdateNotification.notificationType)) { // A subscription is first purchased AppStoreNotificationType.INITIAL_BUY -> { @@ -119,14 +119,14 @@ class AppStoreSubscriptionService(private val userAppClient: UserAppClient, priv } // a customer downgrades - AppStoreNotificationType.DID_CHANGE_RENEWAL_PREF -> { + AppStoreNotificationType.DID_CHANGE_RENEWAL_PREF, AppStoreNotificationType.DID_CHANGE_RENEWAL_STATUS -> { // auto_renewal_product_id - product customer will auto renew at - + // skipping it // latest_receipt_info.original_transaction_id } // customer support issues a refund - AppStoreNotificationType.CANCEL -> { + AppStoreNotificationType.CANCEL, AppStoreNotificationType.REVOKE, AppStoreNotificationType.REFUND -> { // suspend service with a cancellation date? notificationType = NotificationType.SUBSCRIPTION_CANCELED @@ -158,12 +158,20 @@ class AppStoreSubscriptionService(private val userAppClient: UserAppClient, priv // latest_receipt_info.expires_date_ms - date when the subscription will expire } - AppStoreNotificationType.DID_CHANGE_RENEWAL_STATUS -> { - // skipping it + AppStoreNotificationType.DID_RENEW -> { + // restore service for a renewed subscription + // update customer's subscription to active / subscribe + + notificationType = NotificationType.SUBSCRIPTION_RENEWED; } - AppStoreNotificationType.PRICE_INCREASE_CONSENT -> { - // skipping it + null -> { + throw IllegalStateException("Missing or not recognised notification type.") + } + + else -> { + logger.warn { "Notification type $appStoreNotificationType is not supported." } + return null } } @@ -186,6 +194,9 @@ class AppStoreSubscriptionService(private val userAppClient: UserAppClient, priv return userAppClient.sendSubscriptionNotification(notification) } + private fun parseAppStoreNotificationTypeEnum(appStoreNotificationValue: String?): AppStoreNotificationType? = + AppStoreNotificationType.values().find { it.name.equals(appStoreNotificationValue, ignoreCase = true) } + fun verifyReceipt(tenant: String?, receiptRequest: ReceiptRequest) = appStoreClient(tenant).verifyReceipt(receiptRequest) private fun appStoreClient(tenant: String?) = diff --git a/src/test/kotlin/com/dietmap/yaak/api/appstore/subscription/SubscriptionControllerTest.kt b/src/test/kotlin/com/dietmap/yaak/api/appstore/subscription/SubscriptionControllerTest.kt index 963c729..e98fef1 100644 --- a/src/test/kotlin/com/dietmap/yaak/api/appstore/subscription/SubscriptionControllerTest.kt +++ b/src/test/kotlin/com/dietmap/yaak/api/appstore/subscription/SubscriptionControllerTest.kt @@ -26,7 +26,7 @@ internal class SubscriptionControllerTest : SupportController() { private val testStatusUpdateNotification: StatusUpdateNotification = StatusUpdateNotification( - "sandbox", AppStoreNotificationType.CANCEL, "cancellationDate", latestReceiptInfo, "", + "sandbox", "CANCEL", "cancellationDate", latestReceiptInfo, "", "latestExpiredReceipt", true, "", "autoRenewProductId", "autoRenewStatusChangeDate", 12323230, unifiedReceipt)