Skip to content

Commit 6164819

Browse files
Implemented QuickCheckout component (OFFLINE-GmbH#616)
1 parent fb74fba commit 6164819

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+2636
-1459
lines changed

classes/payments/PaymentProvider.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
namespace OFFLINE\Mall\Classes\Payments;
44

55
use October\Rain\Exception\ValidationException;
6+
use October\Rain\Parse\Twig;
7+
use OFFLINE\Mall\Models\Cart;
68
use OFFLINE\Mall\Models\Order;
79
use OFFLINE\Mall\Models\PaymentGatewaySettings;
810
use Request;
@@ -97,6 +99,55 @@ public function encryptedSettings(): array
9799
return [];
98100
}
99101

102+
/**
103+
* Name of the payment form partial.
104+
*
105+
* @return string
106+
*/
107+
public function paymentFormPartial(): string
108+
{
109+
return 'form';
110+
}
111+
112+
/**
113+
* Name of the customer methods partial.
114+
*
115+
* @return string
116+
*/
117+
public function customerMethodsPartial(): string
118+
{
119+
return 'customermethods';
120+
}
121+
122+
/**
123+
* Renders the payment form partial.
124+
*
125+
* @param Cart|Order $cartOrOrder
126+
*
127+
* @return string
128+
*/
129+
public function renderPaymentForm($cartOrOrder): string
130+
{
131+
$override = themes_path(sprintf('partials/mall/payments/%s/%s.htm',
132+
$this->identifier(),
133+
$this->paymentFormPartial()
134+
));
135+
136+
if (file_exists($override)) {
137+
return (new Twig)->parse(file_get_contents($override), ['cart' => $cartOrOrder]);
138+
}
139+
140+
$fallback = plugins_path(sprintf(
141+
'offline/mall/classes/payments/%s/%s.htm',
142+
$this->identifier(),
143+
$this->paymentFormPartial()
144+
));
145+
146+
return file_exists($fallback)
147+
? (new Twig)->parse(file_get_contents($fallback), ['cart' => $cartOrOrder])
148+
: '';
149+
}
150+
100151
/**
101152
* Set the order that is being paid.
102153
*

classes/payments/stripe/form.htm

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<div class="mall-form-control">
2+
<label class="mall-label">{{ 'offline.mall::frontend.payment_method.card_info' | trans }}</label>
3+
<div id="card-element" class="mall-input mall-input--width-limited">
4+
{{ 'offline.mall::frontend.please_wait' | trans }}
5+
</div>
6+
<div id="card-errors" data-validate-for="stripeToken"></div>
7+
8+
<input type="hidden" class="js-mall-checkout-trigger" name="payment_data[token]" value="" id="stripe-token">
9+
</div>
10+
11+
<script>
12+
$(function () {
13+
14+
{# Lazy load stripe.js to ensure everything works when changing the payment method. #}
15+
var s = document.createElement('script');
16+
s.type = 'text/javascript';
17+
s.src = 'https://js.stripe.com/v3/';
18+
s.onload = initStripe;
19+
20+
document.head.appendChild(s)
21+
22+
function initStripe () {
23+
var stripe = Stripe('{{ cart.payment_method.settings.stripe_publishable_key }}');
24+
var elements = stripe.elements();
25+
26+
var card = elements.create('card');
27+
card.mount('#card-element');
28+
29+
card.addEventListener('change', function (event) {
30+
var displayError = document.getElementById('card-errors');
31+
if (event.error) {
32+
displayError.textContent = event.error.message;
33+
} else {
34+
displayError.textContent = '';
35+
}
36+
});
37+
38+
window.Mall.Callbacks.Checkout.Stripe = function () {
39+
return new Promise(function (resolve, reject) {
40+
generateToken().then(resolve).catch(reject)
41+
}
42+
)
43+
}
44+
45+
var form = document.getElementById('mall-payment-form');
46+
if (form) {
47+
form.addEventListener('submit', generateToken);
48+
}
49+
50+
var input = document.getElementById('stripe-token')
51+
52+
function generateToken (event) {
53+
if (event) {
54+
event.preventDefault();
55+
}
56+
var errorElement = document.getElementById('card-errors');
57+
errorElement.classList.remove('visible')
58+
if (form) {
59+
var submit = form.querySelector('[type="submit"]')
60+
if (submit) {
61+
submit.classList.add('oc-loading')
62+
submit.disabled = true
63+
}
64+
}
65+
return new Promise(function (resolve, reject) {
66+
stripe.createToken(card).then(function (result) {
67+
if (result.error) {
68+
errorElement.textContent = result.error.message;
69+
errorElement.classList.add('visible')
70+
if (submit) {
71+
submit.classList.remove('oc-loading')
72+
submit.disabled = false
73+
}
74+
reject(result.error.message)
75+
} else {
76+
stripeTokenHandler(result.token);
77+
resolve()
78+
}
79+
});
80+
})
81+
}
82+
83+
function stripeTokenHandler (token) {
84+
input.value = token.id
85+
var $form = $('#mall-payment-form')
86+
if ($form.length) {
87+
$form.request('onSubmit')
88+
}
89+
}
90+
}
91+
});
92+
</script>

classes/registration/BootComponents.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use OFFLINE\Mall\Components\ProductReviews;
2020
use OFFLINE\Mall\Components\Products as ProductsComponent;
2121
use OFFLINE\Mall\Components\ProductsFilter;
22+
use OFFLINE\Mall\Components\QuickCheckout;
2223
use OFFLINE\Mall\Components\ShippingMethodSelector;
2324
use OFFLINE\Mall\Components\SignUp;
2425
use OFFLINE\Mall\Components\WishlistButton;
@@ -38,6 +39,7 @@ public function registerComponents()
3839
AddressForm::class => 'addressForm',
3940
PaymentMethodSelector::class => 'paymentMethodSelector',
4041
Checkout::class => 'checkout',
42+
QuickCheckout::class => 'quickCheckout',
4143
ProductsComponent::class => 'products',
4244
ProductsFilter::class => 'productsFilter',
4345
ProductComponent::class => 'product',

components/Checkout.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,14 @@ protected function setData()
154154
$cart->setPaymentMethod(PaymentMethod::getDefault());
155155
}
156156
$this->setVar('cart', $cart);
157-
$this->setVar('paymentMethod', PaymentMethod::find($cart->payment_method_id) ?? PaymentMethod::getDefault());
157+
158+
$paymentMethod = PaymentMethod::find($cart->payment_method_id);
159+
if ( ! $paymentMethod) {
160+
$paymentMethod = PaymentMethod::getDefault();
161+
$cart->setPaymentMethod($paymentMethod);
162+
}
163+
164+
$this->setVar('paymentMethod', $paymentMethod);
158165
$this->setVar('step', $this->property('step'));
159166
$this->setVar('accountPage', GeneralSettings::get('account_page'));
160167

components/PaymentMethodSelector.php

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,26 @@ public function onUseCustomerPaymentMethod()
226226
return $this->doRedirect($gateway, $data);
227227
}
228228

229+
/**
230+
* Renders the payment form of the currently selected
231+
* payment method.
232+
*
233+
* @return string
234+
*/
235+
public function renderPaymentForm()
236+
{
237+
if ( ! $this->workingOnModel->payment_method) {
238+
return '';
239+
}
240+
241+
/** @var PaymentGateway $gateway */
242+
$gateway = app(PaymentGateway::class);
243+
244+
return $gateway
245+
->getProviderById($this->workingOnModel->payment_method->payment_provider)
246+
->renderPaymentForm($this->workingOnModel);
247+
}
248+
229249
/**
230250
* Get the URL to a specific checkout step.
231251
*
@@ -281,7 +301,7 @@ protected function doRedirect(PaymentGateway $gateway, $data)
281301
return $paymentService->process('payment');
282302
}
283303

284-
// Just to prevent any data leakage we store payment information encrypted to the session.
304+
// To prevent any data leakage we store payment information encrypted in the session.
285305
session()->put('mall.payment_method.data', encrypt(json_encode($data)));
286306

287307
$nextStep = request()->get('via') === 'confirm' ? 'confirm' : 'shipping';
@@ -303,8 +323,6 @@ protected function doRedirect(PaymentGateway $gateway, $data)
303323
*/
304324
protected function getPaymentMethod()
305325
{
306-
$paymentMethod = PaymentMethod::findOrFail($this->workingOnModel->payment_method_id);
307-
308-
return $paymentMethod;
326+
return PaymentMethod::findOrFail($this->workingOnModel->payment_method_id);
309327
}
310328
}

0 commit comments

Comments
 (0)