Skip to content

Commit

Permalink
Fix grace period handling
Browse files Browse the repository at this point in the history
- Renewed subscriptions did not create because of subscription meta
assigned to grace period subscription.

- Grace period subscription has to have subscription type of actual
next subscription if user renews payment.

remp/crm#2963
  • Loading branch information
Matus Kalafut committed Oct 19, 2023
1 parent 9847f84 commit 2f97c6d
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 41 deletions.
1 change: 1 addition & 0 deletions src/GooglePlayBillingModule.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class GooglePlayBillingModule extends CrmModule
const META_KEY_PURCHASE_TOKEN = 'google_play_billing_purchase_token';
const META_KEY_ORDER_ID = 'google_play_billing_order_id';
const META_KEY_DEVELOPER_NOTIFICATION_ID = 'google_play_billing_developer_notification_id';
const META_KEY_GRACE_PERIOD_SUBSCRIPTION = 'google_play_billing_grace_period_subscription';

public const USER_SOURCE_APP = 'android-app';

Expand Down
56 changes: 40 additions & 16 deletions src/Hermes/DeveloperNotificationReceivedHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -197,12 +197,24 @@ private function createPayment(SubscriptionResponse $subscriptionResponse, Activ
$subscriptionResponse->getRawResponse()->getOrderId()
);

if ($subscription) {
if ($subscription?->type === SubscriptionsRepository::TYPE_FREE) {
// free trial exists for given SubscriptionResponse,
// do not create payment
return null;
}

if ($subscription) {
$isGraceSubscription = $this->subscriptionMetaRepository->findBySubscriptionAndKey(
$subscription,
GooglePlayBillingModule::META_KEY_GRACE_PERIOD_SUBSCRIPTION
);

if (!$isGraceSubscription) {
// should not get here
throw new \Exception("Subscription already exists for order ID [{$subscriptionResponse->getRawResponse()->getOrderId()}].");
}
}

$user = $this->subscriptionResponseProcessor->getUser($subscriptionResponse, $developerNotification);

if (!in_array($subscriptionResponse->getPaymentState(), [
Expand All @@ -212,7 +224,7 @@ private function createPayment(SubscriptionResponse $subscriptionResponse, Activ
throw new DoNotRetryException("Unable to handle PaymentState [{$subscriptionResponse->getPaymentState()}], no payment created.");
}

$subscriptionType = $this->getSubscriptionType($developerNotification);
$subscriptionType = $this->getSubscriptionType($subscriptionResponse, $developerNotification);

$paymentGatewayCode = GooglePlayBilling::GATEWAY_CODE;
$paymentGateway = $this->paymentGatewaysRepository->findByCode($paymentGatewayCode);
Expand Down Expand Up @@ -275,19 +287,11 @@ private function createPayment(SubscriptionResponse $subscriptionResponse, Activ
}

$recurrentCharge = true;
}

// Handle case when introductory subscription type is different from renewals.
if ($subscriptionType->next_subscription_type_id) {
$googleSubscriptionType = $this->googlePlaySubscriptionTypesRepository->findByGooglePlaySubscriptionId($developerNotification->subscription_id);
if ($googleSubscriptionType->offer_periods) {
$usedOfferPeriods = $this->getUsedOfferPeriods($subscriptionResponse->getRawResponse()->getOrderId());
if ($usedOfferPeriods >= $googleSubscriptionType->offer_periods) {
$subscriptionType = $subscriptionType->next_subscription_type;
}
} else {
$subscriptionType = $subscriptionType->next_subscription_type;
}
}
// this is grace period subscription, set end time to start time of purchased (renewed) subscription
if ($subscription) {
$this->stopGracePeriodSubscription($subscription, $subscriptionStartAt);
}

$paymentItemContainer = (new PaymentItemContainer())
Expand Down Expand Up @@ -434,14 +438,15 @@ public function createGoogleGracePeriodSubscription(

// prepare subscription and subscription meta
$user = $this->subscriptionResponseProcessor->getUser($subscriptionResponse, $developerNotification);
$subscriptionType = $this->getSubscriptionType($developerNotification);
$subscriptionType = $this->getSubscriptionType($subscriptionResponse, $developerNotification);
$subscriptionStartAt = $this->subscriptionResponseProcessor->getSubscriptionStartAt($subscriptionResponse);
$subscriptionEndAt = $this->subscriptionResponseProcessor->getSubscriptionEndAt($subscriptionResponse);

$metas = [
GooglePlayBillingModule::META_KEY_PURCHASE_TOKEN => $developerNotification->purchase_token,
GooglePlayBillingModule::META_KEY_ORDER_ID => $subscriptionResponse->getRawResponse()->getOrderId(),
GooglePlayBillingModule::META_KEY_DEVELOPER_NOTIFICATION_ID => $developerNotification->id,
GooglePlayBillingModule::META_KEY_GRACE_PERIOD_SUBSCRIPTION => true,
];

// get last subscription (with or without payment) linked to grace period through purchase token
Expand Down Expand Up @@ -505,12 +510,31 @@ public function createGoogleGracePeriodSubscription(
return $subscription;
}

public function getSubscriptionType(ActiveRow $developerNotification): ActiveRow
private function stopGracePeriodSubscription(ActiveRow $subscription, $endTime)
{
$this->subscriptionsRepository->update($subscription, [
'end_time' => $endTime
]);
}

public function getSubscriptionType(SubscriptionResponse $subscriptionResponse, ActiveRow $developerNotification): ActiveRow
{
$googlePlaySubscriptionType = $this->googlePlaySubscriptionTypesRepository->findByGooglePlaySubscriptionId($developerNotification->subscription_id);
if (!$googlePlaySubscriptionType || !isset($googlePlaySubscriptionType->subscription_type)) {
throw new \Exception("Unable to find SubscriptionType with code [{$developerNotification->subscription_id}] provided by DeveloperNotification.");
}

// Handle case when introductory subscription type is different from renewals.
if ($googlePlaySubscriptionType->subscription_type->next_subscription_type_id) {
$usedOfferPeriods = $this->getUsedOfferPeriods($subscriptionResponse->getRawResponse()->getOrderId());
// used all offer periods
// if offer periods not set -> there is 1 offer period and we have used it already
if (($googlePlaySubscriptionType->offer_periods && $usedOfferPeriods >= $googlePlaySubscriptionType->offer_periods)
|| (is_null($googlePlaySubscriptionType->offer_periods) && $usedOfferPeriods > 0)) {
return $googlePlaySubscriptionType->subscription_type->next_subscription_type;
}
}

return $googlePlaySubscriptionType->subscription_type;
}

Expand Down
Loading

0 comments on commit 2f97c6d

Please sign in to comment.