Skip to content

Commit

Permalink
Merge pull request #304 from craftcms/feature/subscription-billing-is…
Browse files Browse the repository at this point in the history
…sues-fixes

Subscription billing issue fixes
  • Loading branch information
lukeholder authored Jul 17, 2024
2 parents 8bbed42 + c388cec commit 9e892a3
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 32 deletions.
31 changes: 16 additions & 15 deletions src/base/Gateway.php
Original file line number Diff line number Diff line change
Expand Up @@ -450,25 +450,26 @@ public function supportsWebhooks(): bool
*/
public function getTransactionHashFromWebhook(): ?string
{
$rawData = Craft::$app->getRequest()->getRawBody();
if (!$rawData) {
return null;
}
$transactionHash = null;

$rawData = Craft::$app->getRequest()->getRawBody();
$data = Json::decodeIfJson($rawData);
if (!$data) {
return null;
}

if ($data) {
$transactionHash = ArrayHelper::getValue($data, 'data.object.metadata.transaction_reference');
if (!$transactionHash || !is_string($transactionHash)) {
$transactionHash = null;
}

$transactionHash = ArrayHelper::getValue($data, 'data.object.metadata.transaction_reference');
if (!$transactionHash || !is_string($transactionHash)) {
$transactionHash = null;
}
if (!$transactionHash) {
$transactionHash = ArrayHelper::getValue($data, 'data.object.payment_intent');
}

if (!$transactionHash) {
// Use the object ID as the unique ID of the stripe object for the transaction hash so we can enforce a mutex
// in \craft\commerce\services\Webhooks::processWebhook() which call this method.
$transactionHash = ArrayHelper::getValue($data, 'data.object.id');
if (!$transactionHash) {
// Use the object ID as the unique ID of the stripe object for the transaction hash so we can enforce a mutex
// in \craft\commerce\services\Webhooks::processWebhook() which call this method.
$transactionHash = ArrayHelper::getValue($data, 'data.object.id');
}
}

return $transactionHash;
Expand Down
5 changes: 3 additions & 2 deletions src/gateways/PaymentIntents.php
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ public function getPaymentFormHtml(array $params): ?string
{
$defaults = [
'clientSecret' => '',
'subscription' => '',
'scenario' => 'payment',
'order' => null,
'gateway' => $this,
Expand Down Expand Up @@ -322,7 +323,7 @@ public function createPaymentSource(BasePaymentForm $sourceData, int $customerId
{
// Is Craft request the commerce/pay controller action?
$appRequest = Craft::$app->getRequest();
$isCommercePayRequest = $appRequest->getIsSiteRequest() && $appRequest->getIsActionRequest() && $appRequest->getActionSegments() == ['commerce', 'pay', 'index'];
$isCommercePayRequest = $appRequest->getIsSiteRequest() && $appRequest->getIsActionRequest() && $appRequest->getActionSegments() == ['commerce', 'payments', 'pay'];

if ($isCommercePayRequest) {
throw new PaymentSourceCreatedLaterException(Craft::t('commerce', 'The payment source should be created after successful payment.'));
Expand Down Expand Up @@ -477,7 +478,7 @@ public function getBillingIssueResolveFormHtml(Subscription $subscription): stri
case 'requires_confirmation':
return $this->getPaymentFormHtml(['clientSecret' => $clientSecret]);
case 'requires_action':
return $this->getPaymentFormHtml(['clientSecret' => $clientSecret, 'scenario' => 'requires_action']);
return $this->getPaymentFormHtml(['clientSecret' => $clientSecret, 'scenario' => 'requires_action', 'subscription' => $subscription->uid]);
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/templates/paymentForms/elementsForm.twig
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<div class="stripe-payment-elements-form hidden"
data-publishablekey="{{ gateway.getPublishableKey() }}"
data-complete-payment-action-url="{{ actionUrl('commerce/payments/complete-payment') }}"
data-complete-subscription-action-url="{{ actionUrl('commerce/subscriptions/complete-subscription') }}"
data-confirm-setup-intent-url="{{ actionUrl('commerce-stripe/customers/confirm-setup-intent') }}"
data-payment-form-namespace="{{ handle|commercePaymentFormNamespace }}"
data-gateway-id="{{ gateway.id }}"
Expand All @@ -14,6 +15,7 @@
data-client-scenario="{{ scenario }}"
data-processing-button-text="{{ processingButtonText }}"
data-hidden-class="{{ hiddenClass }}"
data-subscription="{{ subscription }}"
>
<div class="hidden stripe-error-message {{ errorMessageClasses }}">
{# Error messages are displayed to your customers, here. #}
Expand Down
39 changes: 24 additions & 15 deletions src/web/assets/elementsform/js/paymentForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ class PaymentIntentsElements {
this.stripeInstance = Stripe(publishableKey);
this.elements = null;
this.scenario = this.container.dataset.clientScenario;
this.completeActionUrl = this.container.dataset.completePaymentActionUrl;
this.completePaymentActionUrl =
this.container.dataset.completePaymentActionUrl;
this.completeSubscriptionActionUrl =
this.container.dataset.completeSubscriptionActionUrl;
this.subscription = this.container.dataset.subscription;
this.processingButtonText = this.container.dataset.processingButtonText;
this.hiddenClass = this.container.dataset.hiddenClass;
this.$submitButton = this.container.querySelector(
Expand Down Expand Up @@ -57,20 +61,25 @@ class PaymentIntentsElements {
}

async requiresActionFlow() {
const options = {
clientSecret: this.container.dataset.clientSecret,
appearance: JSON.parse(this.container.dataset.appearance),
};

this.createStripeElementsForm(options);
const completeSubscriptionActionUrl = new URL(
this.completeSubscriptionActionUrl
);
completeSubscriptionActionUrl.searchParams.append(
'subscription',
this.subscription
);
this.completeSubscriptionActionUrl =
completeSubscriptionActionUrl.toString();

const elements = this.elements;
const {error} = await this.stripeInstance.confirmPayment({
elements,
clientSecret: this.container.dataset.clientSecret,
confirmParams: {
return_url: this.completeActionUrl,
return_url: this.completeSubscriptionActionUrl,
},
});

this.showErrorMessage(error.message);
this.$submitButton.classList.add(this.hiddenClass);
}

deprecatedSubscribeFlow() {
Expand Down Expand Up @@ -193,16 +202,16 @@ class PaymentIntentsElements {
return;
}

const completeActionUrl = new URL(this.completeActionUrl);
completeActionUrl.searchParams.append(
const completePaymentActionUrl = new URL(this.completePaymentActionUrl);
completePaymentActionUrl.searchParams.append(
'commerceTransactionHash',
json.transactionHash
);
completeActionUrl.searchParams.append(
completePaymentActionUrl.searchParams.append(
'commerceTransactionId',
json.transactionId
);
this.completeActionUrl = completeActionUrl.toString();
this.completePaymentActionUrl = completePaymentActionUrl.toString();

const options = {
clientSecret: json.redirectData.client_secret,
Expand Down Expand Up @@ -277,7 +286,7 @@ class PaymentIntentsElements {
const {error} = await this.stripeInstance.confirmPayment({
elements,
confirmParams: {
return_url: this.completeActionUrl,
return_url: this.completePaymentActionUrl,
},
});
this.$submitButton.innerText = submitText;
Expand Down

0 comments on commit 9e892a3

Please sign in to comment.