Skip to content

Commit

Permalink
Merge pull request #63 from dietmap/add-free-trial-info-to-notifications
Browse files Browse the repository at this point in the history
update status notification model and add free trial info to server no…
  • Loading branch information
mhuta authored Nov 10, 2022
2 parents cda3b37 + 6d82bc5 commit d7c8f55
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,20 @@ import com.fasterxml.jackson.annotation.JsonProperty
import java.io.Serializable

data class StatusUpdateNotification(
@get:JsonProperty("environment") val environment: String?,
@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?,
@get:JsonProperty("auto_renew_adam_id") val autoRenewAdamId: String?,
@get:JsonProperty("auto_renew_status") val autoRenewStatus: Boolean?,
@get:JsonProperty("auto_renew_product_id") val autoRenewProductId: String?,
@get:JsonProperty("auto_renew_status") val autoRenewStatus: Boolean?,
@get:JsonProperty("auto_renew_status_change_date") val autoRenewStatusChangeDate: String?,
@get:JsonProperty("auto_renew_status_change_date_pst") val autoRenewStatusChangeDatePst: String?,
@get:JsonProperty("auto_renew_status_change_date_ms") val autoRenewStatusChangeDateMs: Long?,
@get:JsonProperty("unified_receipt") val unifiedReceipt: UnifiedReceipt?
@get:JsonProperty("auto_renew_status_change_date_pst") val autoRenewStatusChangeDatePst: String?,
@get:JsonProperty("bid") val bid: String?,
@get:JsonProperty("bvrs") val bvrs: String?,
@get:JsonProperty("environment") val environment: String?,
@get:JsonProperty("expiration_intent") val expirationIntent: String?,
@get:JsonProperty("notification_type") val notificationType: String?,
@get:JsonProperty("original_transaction_id") val originalTransactionId: String?,
@get:JsonProperty("password") val password: String?,
@get:JsonProperty("unified_receipt") val unifiedReceipt: UnifiedReceipt
) : Serializable


Expand All @@ -27,7 +29,7 @@ data class UnifiedReceipt(
@get:JsonProperty("latest_receipt") val latestReceipt: String?,
@get:JsonProperty("latest_receipt_info") val latestReceiptInfo: Collection<LatestReceiptInfo>?,
@get:JsonProperty("pending_renewal_info") val pendingRenewalInfo: Collection<PendingRenewalInfo>?,
@get:JsonProperty("status") val latestExpiredReceiptInfo: String?
@get:JsonProperty("status") val status: String?
) : Serializable


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class SubscriptionController(private val subscriptionService: AppStoreSubscripti
}

/**
* Handler for Server 2 Server notifications
* Handler for Server 2 Server notifications in version 1
*/
@PostMapping("/statusUpdateNotification")
fun handleStatusUpdateNotification(@Valid @RequestBody statusUpdateNotification: StatusUpdateNotification): ResponseEntity<Any> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,10 @@ class AppStoreSubscriptionService(private val userAppClient: UserAppClient, priv

fun handleInitialPurchase(tenant: String?, subscriptionPurchaseRequest: SubscriptionPurchaseRequest) : UserAppSubscriptionOrder? {
val receiptResponse = appStoreClient(tenant).verifyReceipt(ReceiptRequest(subscriptionPurchaseRequest.receipt))

logger.debug { "handleInitialPurchase: ReceiptResponse: $receiptResponse" }

if (receiptResponse.isValid()) {

val latestReceiptInfo = receiptResponse.latestReceiptInfo!!.stream().findFirst().get()

var effectivePrice = subscriptionPurchaseRequest.price

// intro offer period purchase
if (latestReceiptInfo.isInIntroOfferPeriod && subscriptionPurchaseRequest.effectivePrice != null) {
effectivePrice = subscriptionPurchaseRequest.effectivePrice
Expand All @@ -48,7 +43,8 @@ class AppStoreSubscriptionService(private val userAppClient: UserAppClient, priv
appMarketplace = AppMarketplace.APP_STORE,
expiryTimeMillis = latestReceiptInfo.expiresDateMs,
discountCode = subscriptionPurchaseRequest.discountCode,
appStoreReceipt = subscriptionPurchaseRequest.receipt
appStoreReceipt = subscriptionPurchaseRequest.receipt,
isTrialPeriod = latestReceiptInfo.isTrialPeriod
)

return userAppClient.sendSubscriptionNotification(notification)
Expand All @@ -61,15 +57,10 @@ class AppStoreSubscriptionService(private val userAppClient: UserAppClient, priv

fun handleAutoRenewal(tenant: String?, subscriptionRenewRequest: SubscriptionRenewRequest) {
val receiptResponse = appStoreClient(tenant).verifyReceipt(ReceiptRequest(subscriptionRenewRequest.receipt))

logger.debug { "handleAutoRenewal: ReceiptResponse: $receiptResponse" }

if (receiptResponse.isValid()) {

if (receiptResponse.latestReceiptInfo != null) {

val latestReceiptInfo = receiptResponse.latestReceiptInfo.stream().findFirst().get()

val notification = UserAppSubscriptionNotification(
notificationType = NotificationType.SUBSCRIPTION_RENEWED,
description = "Subscription renewal from AppStore",
Expand All @@ -81,11 +72,11 @@ class AppStoreSubscriptionService(private val userAppClient: UserAppClient, priv
countryCode = null,
currencyCode = null,
discountCode = subscriptionRenewRequest.discountCode,
appStoreReceipt = subscriptionRenewRequest.receipt
appStoreReceipt = subscriptionRenewRequest.receipt,
isTrialPeriod = latestReceiptInfo.isTrialPeriod
)

val subscriptionOrder = userAppClient.sendSubscriptionNotification(notification);

checkArgument(subscriptionOrder != null) { "Could not process SubscriptionRenewRequest $subscriptionRenewRequest in user app" }
}
} else {
Expand All @@ -99,7 +90,7 @@ class AppStoreSubscriptionService(private val userAppClient: UserAppClient, priv
logger.debug { "Processing StatusUpdateNotification: ${statusUpdateNotification.notificationType}" }

var notificationType = NotificationType.SUBSCRIPTION_PURCHASED
val latestReceiptInfo = statusUpdateNotification.latestReceiptInfo
val latestReceiptInfo = statusUpdateNotification.unifiedReceipt.latestReceiptInfo?.maxBy { it.purchaseDateMs }!!

when (val appStoreNotificationType = parseAppStoreNotificationTypeEnum(statusUpdateNotification.notificationType)) {

Expand All @@ -119,7 +110,7 @@ class AppStoreSubscriptionService(private val userAppClient: UserAppClient, priv
}

// a customer downgrades
AppStoreNotificationType.DID_CHANGE_RENEWAL_PREF, AppStoreNotificationType.DID_CHANGE_RENEWAL_STATUS -> {
AppStoreNotificationType.DID_CHANGE_RENEWAL_PREF -> {
// auto_renewal_product_id - product customer will auto renew at
// skipping it
// latest_receipt_info.original_transaction_id
Expand Down Expand Up @@ -158,7 +149,7 @@ class AppStoreSubscriptionService(private val userAppClient: UserAppClient, priv
// latest_receipt_info.expires_date_ms - date when the subscription will expire
}

AppStoreNotificationType.DID_RENEW -> {
AppStoreNotificationType.DID_RENEW, AppStoreNotificationType.DID_CHANGE_RENEWAL_STATUS -> {
// restore service for a renewed subscription
// update customer's subscription to active / subscribe

Expand Down Expand Up @@ -186,7 +177,8 @@ class AppStoreSubscriptionService(private val userAppClient: UserAppClient, priv
countryCode = null,
currencyCode = null,
discountCode = null,
appStoreReceipt = statusUpdateNotification.latestReceipt
appStoreReceipt = statusUpdateNotification.unifiedReceipt.latestReceipt,
isTrialPeriod = latestReceiptInfo.isTrialPeriod
)

logger.debug {"Sending UserAppSubscriptionNotification: $notification" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ class GooglePlaySubscriptionService(val androidPublisherService: AndroidPublishe
orderingUserId = purchaseRequest.orderingUserId ?: subscription[USER_ACCOUNT_ID_KEY] as String?,
discountCode = purchaseRequest.discountCode,
expiryTimeMillis = subscription.expiryTimeMillis,
googlePlayPurchaseDetails = GooglePlayPurchaseDetails(purchaseRequest.packageName, purchaseRequest.subscriptionId, purchaseRequest.purchaseToken)
googlePlayPurchaseDetails = GooglePlayPurchaseDetails(purchaseRequest.packageName, purchaseRequest.subscriptionId, purchaseRequest.purchaseToken),
isTrialPeriod = subscription.paymentState == PAYMENT_FREE_TRIAL_CODE
))

checkArgument(notificationResponse != null) { "Could not create subscription order ${subscription.orderId} in user app" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ data class UserAppSubscriptionNotification(
val orderingUserId: String? = String(),
val discountCode: String? = String(),
val appStoreReceipt: String? = String(),
val googlePlayPurchaseDetails: GooglePlayPurchaseDetails? = null
val googlePlayPurchaseDetails: GooglePlayPurchaseDetails? = null,
val isTrialPeriod: Boolean? = false
)

enum class NotificationType {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,26 @@ internal class SubscriptionControllerTest : SupportController() {
@MockBean
private lateinit var userAppClient: UserAppClient

@Mock
private lateinit var latestReceiptInfo: LatestReceiptInfo

@Mock
private lateinit var unifiedReceipt: UnifiedReceipt


private val testStatusUpdateNotification: StatusUpdateNotification = StatusUpdateNotification(
"sandbox", "CANCEL", "cancellationDate", latestReceiptInfo, "",
"latestExpiredReceipt", true, "", "autoRenewProductId",
"autoRenewStatusChangeDate", 12323230, unifiedReceipt)
"adamId", "sandbox", true, "cancellationDate", 1,
"latestExpiredReceipt", "1", "", "autoRenewProductId",
"autoRenewStatusChangeDate", "CANEL", "123", "123", unifiedReceipt
)

@Test
fun `simulate subscription status update notification`() {
this.mockMvc.perform(
MockMvcRequestBuilders.post("/api/appstore/subscriptions/statusUpdateNotification")
.contentType(MediaType.APPLICATION_JSON)
.content(asJsonString(testStatusUpdateNotification))
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk)
MockMvcRequestBuilders.post("/api/appstore/subscriptions/statusUpdateNotification")
.contentType(MediaType.APPLICATION_JSON)
.content(asJsonString(testStatusUpdateNotification))
.accept(MediaType.APPLICATION_JSON)
)
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk)
}

}

0 comments on commit d7c8f55

Please sign in to comment.