@fleetbase/storefront-engine 0.3.17 → 0.3.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/addon/components/customer-panel/orders.hbs +107 -104
- package/addon/components/customer-panel/orders.js +52 -45
- package/addon/components/modals/incoming-order.hbs +208 -199
- package/addon/components/modals/manage-addons.js +6 -2
- package/addon/components/modals/order-ready-assign-driver.hbs +1 -1
- package/addon/components/order-panel/details.js +0 -2
- package/addon/components/order-panel.hbs +314 -1
- package/addon/components/order-panel.js +51 -3
- package/addon/components/widget/customers.hbs +75 -51
- package/addon/components/widget/customers.js +29 -41
- package/addon/components/widget/orders.hbs +278 -119
- package/addon/components/widget/orders.js +75 -80
- package/addon/components/widget/storefront-metrics.hbs +3 -6
- package/addon/components/widget/storefront-metrics.js +25 -41
- package/addon/controllers/orders/index.js +214 -105
- package/addon/controllers/products/index/category/new.js +2 -1
- package/addon/controllers/products/index/index.js +0 -23
- package/addon/controllers/settings/gateways.js +14 -18
- package/addon/helpers/get-tip-amount.js +13 -2
- package/addon/models/product-addon-category.js +3 -0
- package/addon/routes/application.js +2 -4
- package/addon/services/order-actions.js +248 -0
- package/addon/services/storefront.js +2 -0
- package/addon/styles/storefront-engine.css +55 -0
- package/addon/templates/home.hbs +2 -1
- package/addon/templates/orders/index/view.hbs +1 -1
- package/addon/templates/orders/index.hbs +26 -3
- package/addon/templates/products/index/category/new.hbs +4 -1
- package/addon/templates/products/index/index.hbs +28 -28
- package/addon/templates/settings/gateways.hbs +1 -1
- package/addon/templates/settings/index.hbs +2 -2
- package/addon/templates/settings.hbs +1 -1
- package/app/services/order-actions.js +1 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +1 -1
- package/server/migrations/2023_05_03_025307_create_carts_table.php +1 -1
- package/server/migrations/2023_05_03_025307_create_checkouts_table.php +1 -1
- package/server/migrations/2023_05_03_025307_create_gateways_table.php +1 -1
- package/server/migrations/2023_05_03_025307_create_network_stores_table.php +1 -1
- package/server/migrations/2023_05_03_025307_create_networks_table.php +1 -1
- package/server/migrations/2023_05_03_025307_create_notification_channels_table.php +1 -1
- package/server/migrations/2023_05_03_025307_create_payment_methods_table.php +1 -1
- package/server/migrations/2023_05_03_025307_create_product_addon_categories_table.php +1 -1
- package/server/migrations/2023_05_03_025307_create_product_addons_table.php +1 -1
- package/server/migrations/2023_05_03_025307_create_product_hours_table.php +1 -1
- package/server/migrations/2023_05_03_025307_create_product_store_locations_table.php +1 -1
- package/server/migrations/2023_05_03_025307_create_product_variant_options_table.php +1 -1
- package/server/migrations/2023_05_03_025307_create_product_variants_table.php +1 -1
- package/server/migrations/2023_05_03_025307_create_products_table.php +1 -1
- package/server/migrations/2023_05_03_025307_create_reviews_table.php +1 -1
- package/server/migrations/2023_05_03_025307_create_store_hours_table.php +1 -1
- package/server/migrations/2023_05_03_025307_create_store_locations_table.php +1 -1
- package/server/migrations/2023_05_03_025307_create_stores_table.php +1 -1
- package/server/migrations/2023_05_03_025307_create_votes_table.php +1 -1
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_carts_table.php +1 -1
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_checkouts_table.php +1 -1
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_gateways_table.php +1 -1
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_network_stores_table.php +1 -1
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_networks_table.php +1 -1
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_notification_channels_table.php +1 -1
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_payment_methods_table.php +1 -1
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_addon_categories_table.php +1 -1
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_addons_table.php +1 -1
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_hours_table.php +1 -1
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_store_locations_table.php +1 -1
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_variant_options_table.php +1 -1
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_variants_table.php +1 -1
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_products_table.php +1 -1
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_reviews_table.php +1 -1
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_store_hours_table.php +1 -1
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_store_locations_table.php +1 -1
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_stores_table.php +1 -1
- package/server/migrations/2023_05_03_025310_add_foreign_keys_to_votes_table.php +1 -1
- package/server/src/Http/Controllers/ActionController.php +2 -1
- package/server/src/Http/Controllers/OrderController.php +15 -2
- package/server/src/Http/Controllers/ProductController.php +2 -0
- package/server/src/Http/Controllers/v1/CheckoutController.php +337 -40
- package/server/src/Http/Controllers/v1/CustomerController.php +88 -3
- package/server/src/Http/Controllers/v1/ServiceQuoteController.php +5 -5
- package/server/src/Http/Controllers/v1/StoreController.php +35 -5
- package/server/src/Http/Requests/CreateCustomerRequest.php +4 -0
- package/server/src/Http/Requests/CreateStripeSetupIntentRequest.php +31 -0
- package/server/src/Http/Requests/CustomerRequest.php +31 -0
- package/server/src/Http/Requests/InitializeCheckoutRequest.php +2 -1
- package/server/src/Http/Resources/Cart.php +18 -1
- package/server/src/Http/Resources/Customer.php +19 -14
- package/server/src/Http/Resources/Store.php +5 -1
- package/server/src/Models/AddonCategory.php +14 -16
- package/server/src/Models/Cart.php +10 -5
- package/server/src/Models/Customer.php +2 -2
- package/server/src/Models/Gateway.php +9 -4
- package/server/src/Models/Product.php +9 -10
- package/server/src/Models/ProductAddonCategory.php +2 -0
- package/server/src/Models/Store.php +2 -2
- package/server/src/Observers/OrderObserver.php +7 -1
- package/server/src/Rules/IsValidLocation.php +2 -2
- package/server/src/Support/QPay.php +35 -1
- package/server/src/Support/Storefront.php +34 -0
- package/server/src/Support/StripeUtils.php +38 -0
- package/server/src/routes.php +19 -0
- package/translations/en-us.yaml +8 -2
|
@@ -14,6 +14,7 @@ use Fleetbase\Http\Controllers\Controller;
|
|
|
14
14
|
use Fleetbase\Models\Transaction;
|
|
15
15
|
use Fleetbase\Models\TransactionItem;
|
|
16
16
|
use Fleetbase\Storefront\Http\Requests\CaptureOrderRequest;
|
|
17
|
+
use Fleetbase\Storefront\Http\Requests\CreateStripeSetupIntentRequest;
|
|
17
18
|
use Fleetbase\Storefront\Http\Requests\InitializeCheckoutRequest;
|
|
18
19
|
use Fleetbase\Storefront\Models\Cart;
|
|
19
20
|
use Fleetbase\Storefront\Models\Checkout;
|
|
@@ -25,8 +26,11 @@ use Fleetbase\Storefront\Models\StoreLocation;
|
|
|
25
26
|
use Fleetbase\Storefront\Notifications\StorefrontOrderPreparing;
|
|
26
27
|
use Fleetbase\Storefront\Support\QPay;
|
|
27
28
|
use Fleetbase\Storefront\Support\Storefront;
|
|
29
|
+
use Fleetbase\Storefront\Support\StripeUtils;
|
|
30
|
+
use Fleetbase\Support\SocketCluster\SocketClusterService;
|
|
28
31
|
use Illuminate\Http\Request;
|
|
29
32
|
use Illuminate\Support\Arr;
|
|
33
|
+
use Illuminate\Support\Facades\Log;
|
|
30
34
|
use Illuminate\Support\Str;
|
|
31
35
|
use Stripe\Exception\InvalidRequestException;
|
|
32
36
|
|
|
@@ -41,7 +45,7 @@ class CheckoutController extends Controller
|
|
|
41
45
|
$isCashOnDelivery = $request->input('cash') || $gatewayCode === 'cash';
|
|
42
46
|
$isPickup = $request->input('pickup', false);
|
|
43
47
|
$tip = $request->input('tip', false);
|
|
44
|
-
$deliveryTip = $request->or(['
|
|
48
|
+
$deliveryTip = $request->or(['deliveryTip', 'delivery_tip'], false);
|
|
45
49
|
|
|
46
50
|
// create checkout options
|
|
47
51
|
$checkoutOptions = Utils::createObject([
|
|
@@ -63,7 +67,7 @@ class CheckoutController extends Controller
|
|
|
63
67
|
}
|
|
64
68
|
|
|
65
69
|
if (!$gateway) {
|
|
66
|
-
return response()->
|
|
70
|
+
return response()->apiError('No gateway configured!');
|
|
67
71
|
}
|
|
68
72
|
|
|
69
73
|
// handle checkout initialization based on gateway
|
|
@@ -72,11 +76,11 @@ class CheckoutController extends Controller
|
|
|
72
76
|
}
|
|
73
77
|
|
|
74
78
|
// handle checkout initialization based on gateway
|
|
75
|
-
if ($gateway->
|
|
76
|
-
return static::
|
|
79
|
+
if ($gateway->isQPayGateway) {
|
|
80
|
+
return static::initializeQPayCheckout($customer, $gateway, $serviceQuote, $cart, $checkoutOptions);
|
|
77
81
|
}
|
|
78
82
|
|
|
79
|
-
return response()->
|
|
83
|
+
return response()->apiError('Unable to initialize checkout!');
|
|
80
84
|
}
|
|
81
85
|
|
|
82
86
|
public static function initializeCashCheckout(Contact $customer, Gateway $gateway, ServiceQuote $serviceQuote, Cart $cart, $checkoutOptions)
|
|
@@ -86,7 +90,7 @@ class CheckoutController extends Controller
|
|
|
86
90
|
|
|
87
91
|
// get amount/subtotal
|
|
88
92
|
$amount = static::calculateCheckoutAmount($cart, $serviceQuote, $checkoutOptions);
|
|
89
|
-
$currency = $cart->
|
|
93
|
+
$currency = $cart->getCurrency();
|
|
90
94
|
|
|
91
95
|
// get store id if applicable
|
|
92
96
|
$storeId = session('storefront_store');
|
|
@@ -128,18 +132,18 @@ class CheckoutController extends Controller
|
|
|
128
132
|
]);
|
|
129
133
|
}
|
|
130
134
|
|
|
131
|
-
public static function initializeStripeCheckout(Contact $customer, Gateway $gateway, ServiceQuote $serviceQuote, Cart $cart, $checkoutOptions)
|
|
135
|
+
public static function initializeStripeCheckout(Contact $customer, Gateway $gateway, ?ServiceQuote $serviceQuote, Cart $cart, $checkoutOptions)
|
|
132
136
|
{
|
|
133
137
|
// check if pickup order
|
|
134
138
|
$isPickup = $checkoutOptions->is_pickup;
|
|
135
139
|
|
|
136
140
|
// get amount/subtotal
|
|
137
141
|
$amount = static::calculateCheckoutAmount($cart, $serviceQuote, $checkoutOptions);
|
|
138
|
-
$currency = $cart->
|
|
142
|
+
$currency = $cart->getCurrency();
|
|
139
143
|
|
|
140
144
|
// check for secret key first
|
|
141
145
|
if (!isset($gateway->config->secret_key)) {
|
|
142
|
-
return response()->
|
|
146
|
+
return response()->apiError('Gateway not configured correctly!');
|
|
143
147
|
}
|
|
144
148
|
|
|
145
149
|
// Set the stipre secret key from gateway
|
|
@@ -169,15 +173,27 @@ class CheckoutController extends Controller
|
|
|
169
173
|
['stripe_version' => '2020-08-27']
|
|
170
174
|
);
|
|
171
175
|
} else {
|
|
172
|
-
return response()->
|
|
176
|
+
return response()->apiError('Error from Stripe: ' . $errorMessage);
|
|
173
177
|
}
|
|
174
178
|
}
|
|
175
179
|
|
|
176
|
-
|
|
177
|
-
|
|
180
|
+
// Prepare payment intent data
|
|
181
|
+
$paymentIntentData = [
|
|
182
|
+
'amount' => Utils::formatAmountForStripe($amount, $currency),
|
|
178
183
|
'currency' => $currency,
|
|
179
184
|
'customer' => $customer->getMeta('stripe_id'),
|
|
180
|
-
]
|
|
185
|
+
];
|
|
186
|
+
|
|
187
|
+
// Check if customer has a saved default payment method
|
|
188
|
+
if (StripeUtils::isCustomerPaymentMethodValid($customer)) {
|
|
189
|
+
$paymentIntentData['payment_method'] = $$customer->getMeta('stripe_payment_method_id');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
$paymentIntent = \Stripe\PaymentIntent::create($paymentIntentData);
|
|
194
|
+
} catch (\Exception $e) {
|
|
195
|
+
return response()->apiError($e->getMessage());
|
|
196
|
+
}
|
|
181
197
|
|
|
182
198
|
// create checkout token
|
|
183
199
|
$checkout = Checkout::create([
|
|
@@ -186,7 +202,200 @@ class CheckoutController extends Controller
|
|
|
186
202
|
'network_uuid' => session('storefront_network'),
|
|
187
203
|
'cart_uuid' => $cart->uuid,
|
|
188
204
|
'gateway_uuid' => $gateway->uuid,
|
|
189
|
-
'service_quote_uuid' => $serviceQuote->uuid,
|
|
205
|
+
'service_quote_uuid' => $serviceQuote ? $serviceQuote->uuid : null,
|
|
206
|
+
'owner_uuid' => $customer->uuid,
|
|
207
|
+
'owner_type' => 'fleet-ops:contact',
|
|
208
|
+
'amount' => $amount,
|
|
209
|
+
'currency' => $currency,
|
|
210
|
+
'is_pickup' => $isPickup,
|
|
211
|
+
'options' => $checkoutOptions,
|
|
212
|
+
'cart_state' => $cart->toArray(),
|
|
213
|
+
]);
|
|
214
|
+
|
|
215
|
+
return response()->json([
|
|
216
|
+
'paymentIntent' => $paymentIntent->id,
|
|
217
|
+
'clientSecret' => $paymentIntent->client_secret,
|
|
218
|
+
'ephemeralKey' => $ephemeralKey->secret,
|
|
219
|
+
'customerId' => $customer->getMeta('stripe_id'),
|
|
220
|
+
'token' => $checkout->token,
|
|
221
|
+
]);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
public function createStripeSetupIntentForCustomer(CreateStripeSetupIntentRequest $request)
|
|
225
|
+
{
|
|
226
|
+
$customerId = $request->input('customer');
|
|
227
|
+
$gateway = Storefront::findGateway('stripe');
|
|
228
|
+
|
|
229
|
+
if (!$gateway) {
|
|
230
|
+
return response()->apiError('Stripe not setup.');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
$customer = Customer::findFromCustomerId($customerId);
|
|
234
|
+
|
|
235
|
+
\Stripe\Stripe::setApiKey($gateway->config->secret_key);
|
|
236
|
+
|
|
237
|
+
// Ensure customer has a stripe_id
|
|
238
|
+
if ($customer->missingMeta('stripe_id')) {
|
|
239
|
+
Storefront::createStripeCustomerForContact($customer);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Prepare payment intent data
|
|
243
|
+
$paymentIntentData = [
|
|
244
|
+
'customer' => $customer->getMeta('stripe_id'),
|
|
245
|
+
];
|
|
246
|
+
|
|
247
|
+
// Check if customer has a saved default payment method
|
|
248
|
+
if (StripeUtils::isCustomerPaymentMethodValid($customer)) {
|
|
249
|
+
$paymentIntentData['payment_method'] = $$customer->getMeta('stripe_payment_method_id');
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
try {
|
|
253
|
+
// Create SetupIntent
|
|
254
|
+
$setupIntent = \Stripe\SetupIntent::create($paymentIntentData);
|
|
255
|
+
|
|
256
|
+
$defaultPaymentMethod = null;
|
|
257
|
+
$savedPaymentMethodId = $customer->getMeta('stripe_payment_method_id');
|
|
258
|
+
|
|
259
|
+
if ($savedPaymentMethodId) {
|
|
260
|
+
// Attempt to retrieve the stored payment method from Stripe
|
|
261
|
+
try {
|
|
262
|
+
$pm = \Stripe\PaymentMethod::retrieve($savedPaymentMethodId);
|
|
263
|
+
if ($pm && $pm->customer === $customer->getMeta('stripe_id')) {
|
|
264
|
+
$defaultPaymentMethod = [
|
|
265
|
+
'paymentMethodId' => $pm->id,
|
|
266
|
+
'id' => $pm->id,
|
|
267
|
+
'brand' => Str::title($pm->card->brand),
|
|
268
|
+
'last4' => $pm->card->last4,
|
|
269
|
+
'label' => $pm->card->last4,
|
|
270
|
+
'exp_month' => $pm->card->exp_month,
|
|
271
|
+
'exp_year' => $pm->card->exp_year,
|
|
272
|
+
'country' => $pm->card->country,
|
|
273
|
+
'funding' => $pm->card->funding,
|
|
274
|
+
];
|
|
275
|
+
}
|
|
276
|
+
} catch (\Exception $e) {
|
|
277
|
+
// If retrieval fails, we just won't have a defaultPaymentMethod
|
|
278
|
+
Log::warning('Failed to retrieve saved payment method from Stripe: ' . $e->getMessage());
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return response()->json([
|
|
283
|
+
'setupIntent' => $setupIntent->id,
|
|
284
|
+
'clientSecret' => $setupIntent->client_secret,
|
|
285
|
+
'defaultPaymentMethod' => $defaultPaymentMethod,
|
|
286
|
+
]);
|
|
287
|
+
} catch (\Exception $e) {
|
|
288
|
+
return response()->apiError($e->getMessage());
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
public function updateStripePaymentIntent(Request $request)
|
|
293
|
+
{
|
|
294
|
+
// Extract necessary parameters from request
|
|
295
|
+
$customerId = $request->input('customer');
|
|
296
|
+
$cartId = $request->input('cart');
|
|
297
|
+
$serviceQuoteId = $request->or(['serviceQuote', 'service_quote']);
|
|
298
|
+
$paymentIntentId = $request->or(['paymentIntent', 'paymentIntentId', 'payment_intent_id']);
|
|
299
|
+
$isPickup = $request->input('pickup', false);
|
|
300
|
+
$tip = $request->input('tip', false);
|
|
301
|
+
$deliveryTip = $request->or(['deliveryTip', 'delivery_tip'], false);
|
|
302
|
+
|
|
303
|
+
// Create checkout options from request
|
|
304
|
+
$checkoutOptions = Utils::createObject([
|
|
305
|
+
'is_pickup' => $isPickup,
|
|
306
|
+
'tip' => $tip,
|
|
307
|
+
'delivery_tip' => $deliveryTip,
|
|
308
|
+
]);
|
|
309
|
+
|
|
310
|
+
// Retrieve the gateway (stripe)
|
|
311
|
+
$gateway = Storefront::findGateway('stripe');
|
|
312
|
+
if (!$gateway) {
|
|
313
|
+
return response()->apiError('No stripe gateway configured!');
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Retrieve and validate necessary models
|
|
317
|
+
$cart = Cart::retrieve($cartId);
|
|
318
|
+
if (!$cart) {
|
|
319
|
+
return response()->apiError('Invalid cart ID provided');
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
$customer = Customer::findFromCustomerId($customerId);
|
|
323
|
+
if (!$customer) {
|
|
324
|
+
return response()->apiError('Invalid customer ID provided');
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
$serviceQuote = ServiceQuote::select(['amount', 'meta', 'uuid', 'public_id'])
|
|
328
|
+
->where('public_id', $serviceQuoteId)
|
|
329
|
+
->first();
|
|
330
|
+
|
|
331
|
+
// Recalculate amount based on cart, serviceQuote, and checkoutOptions
|
|
332
|
+
$amount = static::calculateCheckoutAmount($cart, $serviceQuote, $checkoutOptions);
|
|
333
|
+
$currency = $cart->getCurrency();
|
|
334
|
+
|
|
335
|
+
// Check for Stripe secret key
|
|
336
|
+
if (!isset($gateway->config->secret_key)) {
|
|
337
|
+
return response()->apiError('Gateway not configured correctly!');
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Set Stripe API key
|
|
341
|
+
\Stripe\Stripe::setApiKey($gateway->config->secret_key);
|
|
342
|
+
|
|
343
|
+
// Ensure customer has a stripe_id
|
|
344
|
+
if ($customer->missingMeta('stripe_id')) {
|
|
345
|
+
Storefront::createStripeCustomerForContact($customer);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Retrieve the existing PaymentIntent
|
|
349
|
+
try {
|
|
350
|
+
$paymentIntent = \Stripe\PaymentIntent::retrieve($paymentIntentId);
|
|
351
|
+
} catch (\Exception $e) {
|
|
352
|
+
return response()->apiError('Failed to retrieve PaymentIntent: ' . $e->getMessage());
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Check if PaymentIntent is in a modifiable state
|
|
356
|
+
$modifiableStatuses = ['requires_payment_method', 'requires_confirmation', 'requires_action', 'processing'];
|
|
357
|
+
if (!in_array($paymentIntent->status, $modifiableStatuses)) {
|
|
358
|
+
return response()->apiError('PaymentIntent cannot be updated at this stage.');
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Prepare the updated data
|
|
362
|
+
$updateData = [
|
|
363
|
+
'amount' => Utils::formatAmountForStripe($amount, $currency),
|
|
364
|
+
'currency' => $currency,
|
|
365
|
+
];
|
|
366
|
+
|
|
367
|
+
// Update the PaymentIntent
|
|
368
|
+
try {
|
|
369
|
+
$paymentIntent = \Stripe\PaymentIntent::update($paymentIntentId, $updateData);
|
|
370
|
+
} catch (\Exception $e) {
|
|
371
|
+
return response()->apiError('Failed to update PaymentIntent: ' . $e->getMessage());
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// If payment intent has a payment method set already update for the customer
|
|
375
|
+
$paymentIntentPaymentMethodId = $paymentIntent->payment_method;
|
|
376
|
+
$customerPaymentMethodId = $customer->getMeta('stripe_payment_method_id');
|
|
377
|
+
if ($paymentIntentPaymentMethodId !== $customerPaymentMethodId) {
|
|
378
|
+
$customer->updateMeta('stripe_payment_method_id', $paymentIntentPaymentMethodId);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Create a new EphemeralKey if needed for the frontend
|
|
382
|
+
try {
|
|
383
|
+
$ephemeralKey = \Stripe\EphemeralKey::create(
|
|
384
|
+
['customer' => $customer->getMeta('stripe_id')],
|
|
385
|
+
['stripe_version' => '2020-08-27']
|
|
386
|
+
);
|
|
387
|
+
} catch (\Exception $e) {
|
|
388
|
+
return response()->apiError('Failed to create ephemeral key: ' . $e->getMessage());
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Create a new checkout token
|
|
392
|
+
$checkout = Checkout::create([
|
|
393
|
+
'company_uuid' => session('company'),
|
|
394
|
+
'store_uuid' => session('storefront_store'),
|
|
395
|
+
'network_uuid' => session('storefront_network'),
|
|
396
|
+
'cart_uuid' => $cart->uuid,
|
|
397
|
+
'gateway_uuid' => $gateway->uuid,
|
|
398
|
+
'service_quote_uuid' => $serviceQuote ? $serviceQuote->uuid : null,
|
|
190
399
|
'owner_uuid' => $customer->uuid,
|
|
191
400
|
'owner_type' => 'fleet-ops:contact',
|
|
192
401
|
'amount' => $amount,
|
|
@@ -196,15 +405,17 @@ class CheckoutController extends Controller
|
|
|
196
405
|
'cart_state' => $cart->toArray(),
|
|
197
406
|
]);
|
|
198
407
|
|
|
408
|
+
// Return JSON response with updated PaymentIntent and ephemeral key
|
|
199
409
|
return response()->json([
|
|
200
|
-
'paymentIntent' => $paymentIntent->
|
|
410
|
+
'paymentIntent' => $paymentIntent->id,
|
|
411
|
+
'clientSecret' => $paymentIntent->client_secret,
|
|
201
412
|
'ephemeralKey' => $ephemeralKey->secret,
|
|
202
413
|
'customerId' => $customer->getMeta('stripe_id'),
|
|
203
414
|
'token' => $checkout->token,
|
|
204
415
|
]);
|
|
205
416
|
}
|
|
206
417
|
|
|
207
|
-
public static function
|
|
418
|
+
public static function initializeQPayCheckout(Contact $customer, Gateway $gateway, ?ServiceQuote $serviceQuote, Cart $cart, $checkoutOptions)
|
|
208
419
|
{
|
|
209
420
|
// Get store info
|
|
210
421
|
$about = Storefront::about();
|
|
@@ -214,11 +425,11 @@ class CheckoutController extends Controller
|
|
|
214
425
|
|
|
215
426
|
// get amount/subtotal
|
|
216
427
|
$amount = static::calculateCheckoutAmount($cart, $serviceQuote, $checkoutOptions);
|
|
217
|
-
$currency = $cart->
|
|
428
|
+
$currency = $cart->getCurrency();
|
|
218
429
|
|
|
219
430
|
// check for secret key first
|
|
220
431
|
if (!isset($gateway->config->username)) {
|
|
221
|
-
return response()->
|
|
432
|
+
return response()->apiError('Gateway not configured correctly!');
|
|
222
433
|
}
|
|
223
434
|
|
|
224
435
|
// Create qpay instance
|
|
@@ -231,15 +442,8 @@ class CheckoutController extends Controller
|
|
|
231
442
|
// Set auth token
|
|
232
443
|
$qpay = $qpay->setAuthToken();
|
|
233
444
|
|
|
234
|
-
//
|
|
235
|
-
$
|
|
236
|
-
$invoiceCode = $gateway->sandbox ? 'TEST_INVOICE' : $gateway->config->invoice_id;
|
|
237
|
-
$invoiceDescription = $about->name . ' cart checkout';
|
|
238
|
-
$invoiceReceiverCode = $gateway->public_id;
|
|
239
|
-
$senderInvoiceNo = $cart->id;
|
|
240
|
-
|
|
241
|
-
// Create qpay invoice
|
|
242
|
-
$invoice = $qpay->createSimpleInvoice($invoiceAmount, $invoiceCode, $invoiceDescription, $invoiceReceiverCode, $senderInvoiceNo);
|
|
445
|
+
// Test payment
|
|
446
|
+
$testPayment = is_string(data_get($checkoutOptions, 'testPayment')) && $gateway->sandbox;
|
|
243
447
|
|
|
244
448
|
// Create checkout token
|
|
245
449
|
$checkout = Checkout::create([
|
|
@@ -248,7 +452,7 @@ class CheckoutController extends Controller
|
|
|
248
452
|
'network_uuid' => session('storefront_network'),
|
|
249
453
|
'cart_uuid' => $cart->uuid,
|
|
250
454
|
'gateway_uuid' => $gateway->uuid,
|
|
251
|
-
'service_quote_uuid' => $serviceQuote->uuid,
|
|
455
|
+
'service_quote_uuid' => $serviceQuote ? $serviceQuote->uuid : null,
|
|
252
456
|
'owner_uuid' => $customer->uuid,
|
|
253
457
|
'owner_type' => 'fleet-ops:contact',
|
|
254
458
|
'amount' => $amount,
|
|
@@ -258,12 +462,103 @@ class CheckoutController extends Controller
|
|
|
258
462
|
'cart_state' => $cart->toArray(),
|
|
259
463
|
]);
|
|
260
464
|
|
|
465
|
+
// Set QPay Callback
|
|
466
|
+
$callbackParams = ['checkout' => $checkout->public_id];
|
|
467
|
+
if ($testPayment) {
|
|
468
|
+
$callbackParams['test'] = data_get($checkoutOptions, 'testPayment');
|
|
469
|
+
}
|
|
470
|
+
$callbackUrl = Utils::apiUrl('storefront/v1/checkouts/capture-qpay', $callbackParams);
|
|
471
|
+
|
|
472
|
+
// Create invoice description
|
|
473
|
+
// $invoiceAmount = round($amount / 100);
|
|
474
|
+
$invoiceAmount = $amount;
|
|
475
|
+
$invoiceCode = $gateway->sandbox ? 'TEST_INVOICE' : $gateway->config->invoice_id;
|
|
476
|
+
$invoiceDescription = $about->name . ' cart checkout';
|
|
477
|
+
$invoiceReceiverCode = $gateway->public_id;
|
|
478
|
+
$senderInvoiceNo = $cart->id;
|
|
479
|
+
|
|
480
|
+
// Create qpay invoice
|
|
481
|
+
$invoice = $qpay->createSimpleInvoice($invoiceAmount, $invoiceCode, $invoiceDescription, $invoiceReceiverCode, $senderInvoiceNo, $callbackUrl);
|
|
482
|
+
|
|
483
|
+
// Update checkout with invoice id
|
|
484
|
+
$checkout->updateOption('qpay_invoice_id', data_get($invoice, 'invoice_id'));
|
|
485
|
+
|
|
261
486
|
return response()->json([
|
|
262
|
-
'invoice'
|
|
263
|
-
'
|
|
487
|
+
'invoice' => $invoice,
|
|
488
|
+
'checkout' => $checkout->public_id,
|
|
489
|
+
'token' => $checkout->token,
|
|
264
490
|
]);
|
|
265
491
|
}
|
|
266
492
|
|
|
493
|
+
public function captureQPayCallback(Request $request)
|
|
494
|
+
{
|
|
495
|
+
$checkoutId = $request->input('checkout');
|
|
496
|
+
$respond = $request->boolean('respond');
|
|
497
|
+
$test = $request->input('test'); // success, error
|
|
498
|
+
|
|
499
|
+
if ($checkoutId) {
|
|
500
|
+
// Get the checkout instance
|
|
501
|
+
$checkout = Checkout::where('public_id', $checkoutId)->first();
|
|
502
|
+
if ($checkout) {
|
|
503
|
+
// Restore the payment gateway instance
|
|
504
|
+
$gateway = Gateway::where('uuid', $checkout->gateway_uuid)->first();
|
|
505
|
+
if ($gateway) {
|
|
506
|
+
try {
|
|
507
|
+
// If testing send back test data
|
|
508
|
+
// Handle test below
|
|
509
|
+
if ($gateway->sandbox && is_string($test) && in_array($test, ['success', 'error'])) {
|
|
510
|
+
$data = ['error' => null, 'checkout' => $checkout->public_id, 'payment' => null];
|
|
511
|
+
if ($test === 'success') {
|
|
512
|
+
$data['payment'] = QPay::createTestPaymentDataFromCheckout($checkout);
|
|
513
|
+
} else {
|
|
514
|
+
$data['error'] = ['error' => 'PAYMENT_NOT_PAID', 'message' => 'Payment has not paid!'];
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
SocketClusterService::publish('checkout.' . $checkout->public_id, $data);
|
|
518
|
+
if ($respond) {
|
|
519
|
+
return response()->json($data);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// Create qpay instance
|
|
526
|
+
$qpay = QPay::instance($gateway->config->username, $gateway->config->password, $gateway->callback_url);
|
|
527
|
+
if ($gateway->sandbox) {
|
|
528
|
+
$qpay = $qpay->useSandbox();
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// Set auth token
|
|
532
|
+
$qpay = $qpay->setAuthToken();
|
|
533
|
+
|
|
534
|
+
// Check payment status from qpay
|
|
535
|
+
$payment = $qpay->paymentGet($checkoutId);
|
|
536
|
+
if ($payment) {
|
|
537
|
+
$data = [];
|
|
538
|
+
if ($payment->error) {
|
|
539
|
+
$data = ['error' => (array) $payment, 'checkout' => $checkout->public_id, 'payment' => null];
|
|
540
|
+
} else {
|
|
541
|
+
$data = ['payment' =>(array) $payment, 'checkout' => $checkout->public_id, 'error' => null];
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
SocketClusterService::publish('checkout.' . $checkout->public_id, $data);
|
|
545
|
+
if ($respond) {
|
|
546
|
+
return response()->json($data);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
} catch (\Exception $e) {
|
|
550
|
+
Log::error('[QPAY CHECKOUT ERROR: ' . $e->getMessage() . ']', $checkout->toArray());
|
|
551
|
+
if ($respond) {
|
|
552
|
+
return response()->apiError($e->getMessage());
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
return response()->json();
|
|
560
|
+
}
|
|
561
|
+
|
|
267
562
|
/**
|
|
268
563
|
* Process a cart item and create/save an entity.
|
|
269
564
|
*
|
|
@@ -314,8 +609,8 @@ class CheckoutController extends Controller
|
|
|
314
609
|
$customer = $checkout->owner;
|
|
315
610
|
$serviceQuote = $checkout->serviceQuote;
|
|
316
611
|
$gateway = $checkout->is_cod ? Gateway::cash() : $checkout->gateway;
|
|
317
|
-
$origin = $serviceQuote->getMeta('origin', []);
|
|
318
|
-
$destination = $serviceQuote->getMeta('destination');
|
|
612
|
+
$origin = $serviceQuote ? $serviceQuote->getMeta('origin', []) : null;
|
|
613
|
+
$destination = $serviceQuote ? $serviceQuote->getMeta('destination') : null;
|
|
319
614
|
$cart = $checkout->cart;
|
|
320
615
|
|
|
321
616
|
// if cart is null then cart has either been deleted or expired
|
|
@@ -327,7 +622,7 @@ class CheckoutController extends Controller
|
|
|
327
622
|
|
|
328
623
|
// $amount = $checkout->amount ?? ($checkout->is_pickup ? $cart->subtotal : $cart->subtotal + $serviceQuote->amount);
|
|
329
624
|
$amount = static::calculateCheckoutAmount($cart, $serviceQuote, $checkout->options);
|
|
330
|
-
$currency = $checkout->currency ??
|
|
625
|
+
$currency = $checkout->currency ?? $cart->getCurrency();
|
|
331
626
|
$store = $about;
|
|
332
627
|
|
|
333
628
|
// check if order is via network for a single store
|
|
@@ -348,7 +643,7 @@ class CheckoutController extends Controller
|
|
|
348
643
|
|
|
349
644
|
// super rare condition
|
|
350
645
|
if (!$store) {
|
|
351
|
-
return response()->
|
|
646
|
+
return response()->apiError('No storefront in request to capture order!');
|
|
352
647
|
}
|
|
353
648
|
|
|
354
649
|
// prepare for integrated vendor order if applicable
|
|
@@ -360,7 +655,7 @@ class CheckoutController extends Controller
|
|
|
360
655
|
try {
|
|
361
656
|
$integratedVendorOrder = $serviceQuote->integratedVendor->api()->createOrderFromServiceQuote($serviceQuote, $request);
|
|
362
657
|
} catch (\Exception $e) {
|
|
363
|
-
return response()->
|
|
658
|
+
return response()->apiError($e->getMessage());
|
|
364
659
|
}
|
|
365
660
|
}
|
|
366
661
|
|
|
@@ -550,7 +845,9 @@ class CheckoutController extends Controller
|
|
|
550
845
|
Storefront::alertNewOrder($order);
|
|
551
846
|
|
|
552
847
|
// purchase service quote
|
|
553
|
-
|
|
848
|
+
if ($serviceQuote) {
|
|
849
|
+
$order->purchaseQuote($serviceQuote->uuid, $transactionDetails);
|
|
850
|
+
}
|
|
554
851
|
|
|
555
852
|
// if order is auto accepted update status
|
|
556
853
|
if ($about->isOption('auto_accept_orders')) {
|
|
@@ -601,10 +898,10 @@ class CheckoutController extends Controller
|
|
|
601
898
|
$cart = $checkout->cart;
|
|
602
899
|
// $amount = $checkout->amount ?? ($checkout->is_pickup ? $cart->subtotal : $cart->subtotal + $serviceQuote->amount);
|
|
603
900
|
$amount = static::calculateCheckoutAmount($cart, $serviceQuote, $checkout->options);
|
|
604
|
-
$currency = $checkout->currency ??
|
|
901
|
+
$currency = $checkout->currency ?? $cart->getCurrency();
|
|
605
902
|
|
|
606
903
|
if (!$about) {
|
|
607
|
-
return response()->
|
|
904
|
+
return response()->apiError('No network in request to capture order!');
|
|
608
905
|
}
|
|
609
906
|
|
|
610
907
|
// prepare for integrated vendor order if applicable
|
|
@@ -616,7 +913,7 @@ class CheckoutController extends Controller
|
|
|
616
913
|
try {
|
|
617
914
|
$integratedVendorOrder = $serviceQuote->integratedVendor->api()->createOrderFromServiceQuote($serviceQuote, $request);
|
|
618
915
|
} catch (\Exception $e) {
|
|
619
|
-
return response()->
|
|
916
|
+
return response()->apiError($e->getMessage());
|
|
620
917
|
}
|
|
621
918
|
}
|
|
622
919
|
|
|
@@ -901,7 +1198,7 @@ class CheckoutController extends Controller
|
|
|
901
1198
|
*
|
|
902
1199
|
* @param stdClass $checkoutOptions
|
|
903
1200
|
*/
|
|
904
|
-
private static function calculateCheckoutAmount(Cart $cart, ServiceQuote $serviceQuote, $checkoutOptions): int
|
|
1201
|
+
private static function calculateCheckoutAmount(Cart $cart, ?ServiceQuote $serviceQuote, $checkoutOptions): int
|
|
905
1202
|
{
|
|
906
1203
|
// cast checkout options to object always
|
|
907
1204
|
$checkoutOptions = (object) $checkoutOptions;
|