-
Notifications
You must be signed in to change notification settings - Fork 393
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor Stripe payment intent checking and storage (#1892)
This PR looks to improve on how payment intents are stored and how to better prevent overlaps when manually placing orders via Stripe and also via the webhook. ## PaymentIntent storage and reference to carts/orders Currently PaymentIntent information is stored in the Cart model's meta, which is then transferred to the order when created. Whilst this works okay it causes for limitations and also means that if the carts meta is ever updated elsewhere, or the intent information is removed, then it will cause unrecoverable loss. This PR looks to move away from the `payment_intent` key in the meta to a `StripePaymentIntent` model, this allows us more flexibility in how payment intents are handled and reacted on. A StripePaymentIntent will be associated to both a cart and an order. The information we store is now: - `intent_id` - This is the PaymentIntent ID which is provided by Stripe - `status` - The PaymentIntent status - `event_id` - If the PaymentIntent was placed via the webhook, this will be populated with the ID of that event - `processing_at` - When a request to place the order is made, this is populated - `processed_at` - Once the order is placed, this will be populated with the current timestamp ## Preventing overlap Currently we delay sending the job to place the order to the queue by 20 seconds, this is less than ideal, now the payment type will check whether we are already processing this order and if so, not do anything further. This should prevent overlaps regardless of how they are triggered. --------- Co-authored-by: alecritson <[email protected]> Co-authored-by: Glenn Jacobs <[email protected]>
- Loading branch information
1 parent
41d4e85
commit 1f5dd5b
Showing
26 changed files
with
431 additions
and
94 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
28 changes: 28 additions & 0 deletions
28
...ages/stripe/database/migrations/2024_07_31_100000_create_stripe_payment_intents_table.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<?php | ||
|
||
use Illuminate\Database\Schema\Blueprint; | ||
use Illuminate\Support\Facades\Schema; | ||
use Lunar\Base\Migration; | ||
|
||
return new class extends Migration | ||
{ | ||
public function up(): void | ||
{ | ||
Schema::create($this->prefix.'stripe_payment_intents', function (Blueprint $table) { | ||
$table->id(); | ||
$table->foreignId('cart_id')->constrained($this->prefix.'carts'); | ||
$table->foreignId('order_id')->nullable()->constrained($this->prefix.'orders'); | ||
$table->string('intent_id')->index(); | ||
$table->string('status')->nullable(); | ||
$table->string('event_id')->index()->nullable(); | ||
$table->timestamp('processing_at')->nullable(); | ||
$table->timestamp('processed_at')->nullable(); | ||
$table->timestamps(); | ||
}); | ||
} | ||
|
||
public function down(): void | ||
{ | ||
Schema::dropIfExists($this->prefix.'stripe_payment_intents'); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
{ | ||
"id": "pm_1MqLiJLkdIwHu7ixUEgbFdYF", | ||
"object": "payment_method", | ||
"billing_details": { | ||
"address": { | ||
"city": "ACME Land", | ||
"country": "GB", | ||
"line1": "123 ACME Lane", | ||
"line2": null, | ||
"postal_code": "AC1 1ME", | ||
"state": "ACME" | ||
}, | ||
"email": "[email protected]", | ||
"name": "Elma Thudd", | ||
"phone": "1234567" | ||
}, | ||
"card": { | ||
"brand": "visa", | ||
"checks": { | ||
"address_line1_check": null, | ||
"address_postal_code_check": null, | ||
"cvc_check": "unchecked" | ||
}, | ||
"country": "US", | ||
"exp_month": 8, | ||
"exp_year": 2026, | ||
"fingerprint": "mToisGZ01V71BCos", | ||
"funding": "credit", | ||
"generated_from": null, | ||
"last4": "4242", | ||
"networks": { | ||
"available": [ | ||
"visa" | ||
], | ||
"preferred": null | ||
}, | ||
"three_d_secure_usage": { | ||
"supported": true | ||
}, | ||
"wallet": null | ||
}, | ||
"created": 1679945299, | ||
"customer": null, | ||
"livemode": false, | ||
"metadata": {}, | ||
"type": "card" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
<?php | ||
|
||
namespace Lunar\Stripe\Actions; | ||
|
||
use Lunar\Models\Country; | ||
use Lunar\Models\Order; | ||
use Lunar\Models\OrderAddress; | ||
use Lunar\Stripe\Facades\Stripe; | ||
use Stripe\PaymentIntent; | ||
|
||
class StoreAddressInformation | ||
{ | ||
public function store(Order $order, PaymentIntent $paymentIntent) | ||
{ | ||
$billingAddress = $order->billingAddress ?: new OrderAddress([ | ||
'order_id' => $order->id, | ||
'type' => 'billing', | ||
]); | ||
|
||
$shippingAddress = $order->shippingAddress ?: new OrderAddress([ | ||
'order_id' => $order->id, | ||
'type' => 'shipping', | ||
]); | ||
|
||
$paymentMethod = Stripe::getPaymentMethod($paymentIntent->payment_method); | ||
|
||
if ($paymentIntent->shipping && $stripeShipping = $paymentIntent->shipping->address) { | ||
$country = Country::where('iso2', $stripeShipping->country)->first(); | ||
$shippingAddress->first_name = explode(' ', $paymentIntent->shipping->name)[0]; | ||
$shippingAddress->last_name = explode(' ', $paymentIntent->shipping->name)[1] ?? ''; | ||
$shippingAddress->line_one = $stripeShipping->line1; | ||
$shippingAddress->line_two = $stripeShipping->line2; | ||
$shippingAddress->city = $stripeShipping->city; | ||
$shippingAddress->state = $stripeShipping->state; | ||
$shippingAddress->postcode = $stripeShipping->postal_code; | ||
$shippingAddress->country_id = $country?->id; | ||
$shippingAddress->contact_phone = $paymentIntent->shipping->phone; | ||
$shippingAddress->save(); | ||
} | ||
|
||
if ($paymentMethod && $stripeBilling = $paymentMethod->billing_details?->address) { | ||
$country = Country::where('iso2', $stripeBilling->country)->first(); | ||
$billingAddress->first_name = explode(' ', $paymentMethod->billing_details->name)[0]; | ||
$billingAddress->last_name = explode(' ', $paymentMethod->billing_details->name)[1] ?? ''; | ||
$billingAddress->line_one = $stripeBilling->line1; | ||
$billingAddress->line_two = $stripeBilling->line2; | ||
$billingAddress->city = $stripeBilling->city; | ||
$billingAddress->state = $stripeBilling->state; | ||
$billingAddress->postcode = $stripeBilling->postal_code; | ||
$billingAddress->country_id = $country?->id; | ||
$billingAddress->contact_phone = $paymentMethod->billing_details->phone; | ||
$billingAddress->contact_email = $paymentMethod->billing_details->email; | ||
$billingAddress->save(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.