-
-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
386 additions
and
1 deletion.
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
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,222 @@ | ||
<?php | ||
|
||
namespace Igniter\PayRegister\Payments; | ||
|
||
use Admin\Classes\BasePaymentGateway; | ||
use Admin\Models\Orders_model; | ||
use Exception; | ||
use Igniter\Flame\Exception\ApplicationException; | ||
use Igniter\Flame\Traits\EventEmitter; | ||
use Igniter\PayRegister\Traits\PaymentHelpers; | ||
use Illuminate\Support\Facades\Event; | ||
use Illuminate\Support\Facades\Redirect; | ||
use Illuminate\Support\Str; | ||
use Stripe\StripeClient; | ||
|
||
class StripeCheckout extends BasePaymentGateway | ||
{ | ||
use EventEmitter; | ||
use PaymentHelpers; | ||
|
||
public function registerEntryPoints() | ||
{ | ||
return [ | ||
'stripe_webhook' => 'processWebhookUrl', | ||
'stripe_checkout_return_url' => 'processSuccessUrl', | ||
'stripe_checkout_cancel_url' => 'processCancelUrl', | ||
]; | ||
} | ||
|
||
public function isTestMode() | ||
{ | ||
return $this->model->transaction_mode != 'live'; | ||
} | ||
|
||
public function getPublishableKey() | ||
{ | ||
return $this->isTestMode() ? $this->model->test_publishable_key : $this->model->live_publishable_key; | ||
} | ||
|
||
public function getSecretKey() | ||
{ | ||
return $this->isTestMode() ? $this->model->test_secret_key : $this->model->live_secret_key; | ||
} | ||
|
||
public function shouldAuthorizePayment() | ||
{ | ||
return $this->model->transaction_type == 'auth_only'; | ||
} | ||
|
||
public function isApplicable($total, $host) | ||
{ | ||
return $host->order_total <= $total; | ||
} | ||
|
||
/** | ||
* @param array $data | ||
* @param \Admin\Models\Payments_model $host | ||
* @param \Admin\Models\Orders_model $order | ||
* | ||
* @return mixed | ||
*/ | ||
public function processPaymentForm($data, $host, $order) | ||
{ | ||
$this->validatePaymentMethod($order, $host); | ||
|
||
$fields = $this->getPaymentFormFields($order, $data); | ||
|
||
try { | ||
$gateway = $this->createGateway(); | ||
$response = $gateway->checkout->sessions->create($fields); | ||
|
||
return Redirect::to($response->url); | ||
} catch (Exception $ex) { | ||
$order->logPaymentAttempt('Payment error -> '.$ex->getMessage(), 0, $fields, []); | ||
throw new ApplicationException('Sorry, there was an error processing your payment. Please try again later.'); | ||
} | ||
} | ||
|
||
public function processSuccessUrl($params) | ||
{ | ||
$hash = $params[0] ?? null; | ||
$redirectPage = input('redirect'); | ||
$cancelPage = input('cancel'); | ||
|
||
$order = $this->createOrderModel()->whereHash($hash)->first(); | ||
|
||
try { | ||
if (!$hash || !$order instanceof Orders_model) | ||
throw new ApplicationException('No order found'); | ||
|
||
if (!strlen($redirectPage)) | ||
throw new ApplicationException('No redirect page found'); | ||
|
||
if (!strlen($cancelPage)) | ||
throw new ApplicationException('No cancel page found'); | ||
|
||
$paymentMethod = $order->payment_method; | ||
if (!$paymentMethod || $paymentMethod->getGatewayClass() != static::class) | ||
throw new ApplicationException('No valid payment method found'); | ||
|
||
$order->logPaymentAttempt('Payment successful', 1, [], $paymentMethod, true); | ||
$order->updateOrderStatus($paymentMethod->order_status, ['notify' => false]); | ||
$order->markAsPaymentProcessed(); | ||
|
||
return Redirect::to(page_url($redirectPage, [ | ||
'id' => $order->getKey(), | ||
'hash' => $order->hash, | ||
])); | ||
} catch (Exception $ex) { | ||
$order->logPaymentAttempt('Payment error -> '.$ex->getMessage(), 0, [], []); | ||
flash()->warning($ex->getMessage())->important(); | ||
} | ||
|
||
return Redirect::to(page_url($cancelPage)); | ||
} | ||
|
||
public function processCancelUrl($params) | ||
{ | ||
$hash = $params[0] ?? null; | ||
$order = $this->createOrderModel()->whereHash($hash)->first(); | ||
if (!$hash || !$order instanceof Orders_model) | ||
throw new ApplicationException('No order found'); | ||
|
||
if (!strlen($redirectPage = input('redirect'))) | ||
throw new ApplicationException('No redirect page found'); | ||
|
||
$paymentMethod = $order->payment_method; | ||
if (!$paymentMethod || $paymentMethod->getGatewayClass() != static::class) | ||
throw new ApplicationException('No valid payment method found'); | ||
|
||
$order->logPaymentAttempt('Payment canceled by customer', 0, input()); | ||
|
||
return Redirect::to(page_url($redirectPage)); | ||
} | ||
|
||
protected function createGateway() | ||
{ | ||
\Stripe\Stripe::setAppInfo( | ||
'TastyIgniter Stripe', | ||
'1.0.0', | ||
'https://tastyigniter.com/marketplace/item/igniter-payregister', | ||
'pp_partner_JZyCCGR3cOwj9S' // Used by Stripe to identify this integration | ||
); | ||
|
||
$stripeClient = new StripeClient([ | ||
'api_key' => $this->getSecretKey(), | ||
]); | ||
|
||
$this->fireSystemEvent('payregister.stripe.extendClient', [$stripeClient]); | ||
|
||
return $stripeClient; | ||
} | ||
|
||
protected function getPaymentFormFields($order, $data = []) | ||
{ | ||
$cancelUrl = $this->makeEntryPointUrl('stripe_checkout_cancel_url').'/'.$order->hash; | ||
$successUrl = $this->makeEntryPointUrl('stripe_checkout_return_url').'/'.$order->hash; | ||
$successUrl .= '?redirect='.array_get($data, 'successPage').'&cancel='.array_get($data, 'cancelPage'); | ||
|
||
$fields = [ | ||
'line_items' => [ | ||
[ | ||
'price_data' => [ | ||
'currency' => currency()->getUserCurrency(), | ||
// All amounts sent to Stripe must be in integers, representing the lowest currency unit (cents) | ||
'unit_amount_decimal' => number_format($order->order_total, 2, '.', '') * 100, | ||
'product_data' => [ | ||
'name' => 'Test', | ||
], | ||
], | ||
'quantity' => 1, | ||
], | ||
], | ||
'cancel_url' => $cancelUrl.'?redirect='.array_get($data, 'cancelPage'), | ||
'success_url' => $successUrl, | ||
'mode' => 'payment', | ||
'metadata' => [ | ||
'order_id' => $order->order_id, | ||
], | ||
]; | ||
|
||
$this->fireSystemEvent('payregister.stripecheckout.extendFields', [&$fields, $order, $data]); | ||
|
||
return $fields; | ||
} | ||
|
||
public function processWebhookUrl() | ||
{ | ||
if (strtolower(request()->method()) !== 'post') | ||
return response('Request method must be POST', 400); | ||
|
||
$payload = json_decode(request()->getContent(), true); | ||
if (!isset($payload['type']) || !strlen($eventType = $payload['type'])) | ||
return response('Missing webhook event name', 400); | ||
|
||
$eventName = Str::studly(str_replace('.', '_', $eventType)); | ||
$methodName = 'webhookHandle'.$eventName; | ||
|
||
if (method_exists($this, $methodName)) | ||
$this->$methodName($payload); | ||
|
||
Event::fire('payregister.stripecheckout.webhook.handle'.$eventName, [$payload]); | ||
|
||
return response('Webhook Handled'); | ||
} | ||
|
||
protected function webhookHandleCheckoutSessionCompleted($payload) | ||
{ | ||
if ($order = Orders_model::find($payload['data']['object']['metadata']['order_id'])) { | ||
if (!$order->isPaymentProcessed()) { | ||
if ($payload['data']['object']['status'] === 'requires_capture') { | ||
$order->logPaymentAttempt('Payment authorized', 1, [], $payload['data']['object']); | ||
} else { | ||
$order->logPaymentAttempt('Payment successful', 1, [], $payload['data']['object'], true); | ||
} | ||
|
||
$order->updateOrderStatus($this->model->order_status, ['notify' => false]); | ||
$order->markAsPaymentProcessed(); | ||
} | ||
} | ||
} | ||
} |
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,118 @@ | ||
<?php | ||
|
||
return [ | ||
'fields' => [ | ||
'setup' => [ | ||
'type' => 'partial', | ||
'path' => '$/igniter/payregister/payments/stripecheckout/info', | ||
], | ||
'transaction_mode' => [ | ||
'label' => 'lang:igniter.payregister::default.stripecheckout.label_transaction_mode', | ||
'type' => 'radiotoggle', | ||
'default' => 'test', | ||
'span' => 'left', | ||
'options' => [ | ||
'live' => 'lang:igniter.payregister::default.stripecheckout.text_live', | ||
'test' => 'lang:igniter.payregister::default.stripecheckout.text_test', | ||
], | ||
], | ||
'transaction_type' => [ | ||
'label' => 'lang:igniter.payregister::default.stripecheckout.label_transaction_type', | ||
'type' => 'radiotoggle', | ||
'default' => 'auth_capture', | ||
'span' => 'right', | ||
'options' => [ | ||
'auth_capture' => 'lang:igniter.payregister::default.stripecheckout.text_auth_capture', | ||
'auth_only' => 'lang:igniter.payregister::default.stripecheckout.text_auth_only', | ||
], | ||
], | ||
'live_secret_key' => [ | ||
'label' => 'lang:igniter.payregister::default.stripecheckout.label_live_secret_key', | ||
'type' => 'text', | ||
'span' => 'left', | ||
'trigger' => [ | ||
'action' => 'show', | ||
'field' => 'transaction_mode', | ||
'condition' => 'value[live]', | ||
], | ||
], | ||
'live_publishable_key' => [ | ||
'label' => 'lang:igniter.payregister::default.stripecheckout.label_live_publishable_key', | ||
'type' => 'text', | ||
'span' => 'right', | ||
'trigger' => [ | ||
'action' => 'show', | ||
'field' => 'transaction_mode', | ||
'condition' => 'value[live]', | ||
], | ||
], | ||
'test_secret_key' => [ | ||
'label' => 'lang:igniter.payregister::default.stripecheckout.label_test_secret_key', | ||
'type' => 'text', | ||
'span' => 'left', | ||
'trigger' => [ | ||
'action' => 'show', | ||
'field' => 'transaction_mode', | ||
'condition' => 'value[test]', | ||
], | ||
], | ||
'test_publishable_key' => [ | ||
'label' => 'lang:igniter.payregister::default.stripecheckout.label_test_publishable_key', | ||
'type' => 'text', | ||
'span' => 'right', | ||
'trigger' => [ | ||
'action' => 'show', | ||
'field' => 'transaction_mode', | ||
'condition' => 'value[test]', | ||
], | ||
], | ||
'locale_code' => [ | ||
'label' => 'lang:igniter.payregister::default.stripecheckout.label_locale_code', | ||
'type' => 'text', | ||
'span' => 'left', | ||
], | ||
'order_fee_type' => [ | ||
'label' => 'lang:igniter.payregister::default.label_order_fee_type', | ||
'type' => 'radiotoggle', | ||
'span' => 'right', | ||
'cssClass' => 'flex-width', | ||
'default' => 1, | ||
'options' => [ | ||
1 => 'lang:admin::lang.menus.text_fixed_amount', | ||
2 => 'lang:admin::lang.menus.text_percentage', | ||
], | ||
], | ||
'order_fee' => [ | ||
'label' => 'lang:igniter.payregister::default.label_order_fee', | ||
'type' => 'currency', | ||
'span' => 'right', | ||
'cssClass' => 'flex-width', | ||
'default' => 0, | ||
'comment' => 'lang:igniter.payregister::default.help_order_fee', | ||
], | ||
'order_total' => [ | ||
'label' => 'lang:igniter.payregister::default.label_order_total', | ||
'type' => 'currency', | ||
'span' => 'left', | ||
'comment' => 'lang:igniter.payregister::default.help_order_total', | ||
], | ||
'order_status' => [ | ||
'label' => 'lang:igniter.payregister::default.label_order_status', | ||
'type' => 'select', | ||
'options' => [\Admin\Models\Statuses_model::class, 'getDropdownOptionsForOrder'], | ||
'span' => 'right', | ||
'comment' => 'lang:igniter.payregister::default.help_order_status', | ||
], | ||
], | ||
'rules' => [ | ||
['transaction_mode', 'lang:igniter.payregister::default.stripecheckout.label_transaction_mode', 'string'], | ||
['live_secret_key', 'lang:igniter.payregister::default.stripecheckout.label_live_secret_key', 'string'], | ||
['live_publishable_key', 'lang:igniter.payregister::default.stripecheckout.label_live_publishable_key', 'string'], | ||
['test_secret_key', 'lang:igniter.payregister::default.stripecheckout.label_test_secret_key', 'string'], | ||
['test_publishable_key', 'lang:igniter.payregister::default.stripecheckout.label_test_publishable_key', 'string'], | ||
['order_fee_type', 'lang:igniter.payregister::default.label_order_fee_type', 'integer'], | ||
['order_fee', 'lang:igniter.payregister::default.label_order_fee', 'numeric'], | ||
['order_total', 'lang:igniter.payregister::default.label_order_total', 'numeric'], | ||
['order_status', 'lang:igniter.payregister::default.label_order_status', 'integer'], | ||
], | ||
]; |
Oops, something went wrong.