@fleetbase/storefront-engine 0.3.17 → 0.3.18

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.
Files changed (31) hide show
  1. package/addon/components/modals/manage-addons.js +6 -2
  2. package/addon/controllers/products/index/category/new.js +2 -1
  3. package/addon/controllers/products/index/index.js +0 -23
  4. package/addon/controllers/settings/gateways.js +14 -18
  5. package/addon/models/product-addon-category.js +3 -0
  6. package/addon/styles/storefront-engine.css +7 -0
  7. package/addon/templates/products/index/category/new.hbs +4 -1
  8. package/addon/templates/settings/gateways.hbs +1 -1
  9. package/addon/templates/settings/index.hbs +2 -2
  10. package/composer.json +1 -1
  11. package/extension.json +1 -1
  12. package/package.json +1 -1
  13. package/server/src/Http/Controllers/ProductController.php +2 -0
  14. package/server/src/Http/Controllers/v1/CheckoutController.php +331 -33
  15. package/server/src/Http/Controllers/v1/CustomerController.php +77 -0
  16. package/server/src/Http/Controllers/v1/StoreController.php +35 -5
  17. package/server/src/Http/Requests/CreateStripeSetupIntentRequest.php +31 -0
  18. package/server/src/Http/Requests/CustomerRequest.php +31 -0
  19. package/server/src/Http/Resources/Cart.php +18 -1
  20. package/server/src/Http/Resources/Customer.php +19 -14
  21. package/server/src/Http/Resources/Store.php +5 -1
  22. package/server/src/Models/AddonCategory.php +14 -16
  23. package/server/src/Models/Cart.php +10 -5
  24. package/server/src/Models/Customer.php +2 -2
  25. package/server/src/Models/Gateway.php +9 -4
  26. package/server/src/Models/Product.php +9 -10
  27. package/server/src/Models/ProductAddonCategory.php +2 -0
  28. package/server/src/Support/QPay.php +35 -1
  29. package/server/src/Support/StripeUtils.php +38 -0
  30. package/server/src/routes.php +19 -0
  31. package/translations/en-us.yaml +2 -1
@@ -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(['delivery_tip', 'deliveryTip'], false);
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()->error('No gateway configured!');
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->isQpayGateway) {
76
- return static::initializeQpayCheckout($customer, $gateway, $serviceQuote, $cart, $checkoutOptions);
79
+ if ($gateway->isQPayGateway) {
80
+ return static::initializeQPayCheckout($customer, $gateway, $serviceQuote, $cart, $checkoutOptions);
77
81
  }
78
82
 
79
- return response()->error('Unable to initialize checkout!');
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->currency ?? session('storefront_currency');
93
+ $currency = $cart->getCurrency();
90
94
 
91
95
  // get store id if applicable
92
96
  $storeId = session('storefront_store');
@@ -135,11 +139,11 @@ class CheckoutController extends Controller
135
139
 
136
140
  // get amount/subtotal
137
141
  $amount = static::calculateCheckoutAmount($cart, $serviceQuote, $checkoutOptions);
138
- $currency = $cart->currency ?? session('storefront_currency');
142
+ $currency = $cart->getCurrency();
139
143
 
140
144
  // check for secret key first
141
145
  if (!isset($gateway->config->secret_key)) {
142
- return response()->error('Gateway not configured correctly!');
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()->error('Error from Stripe: ' . $errorMessage);
176
+ return response()->apiError('Error from Stripe: ' . $errorMessage);
173
177
  }
174
178
  }
175
179
 
176
- $paymentIntent = \Stripe\PaymentIntent::create([
177
- 'amount' => $amount,
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([
@@ -197,14 +213,212 @@ class CheckoutController extends Controller
197
213
  ]);
198
214
 
199
215
  return response()->json([
200
- 'paymentIntent' => $paymentIntent->client_secret,
216
+ 'paymentIntent' => $paymentIntent->id,
217
+ 'clientSecret' => $paymentIntent->client_secret,
201
218
  'ephemeralKey' => $ephemeralKey->secret,
202
219
  'customerId' => $customer->getMeta('stripe_id'),
203
220
  'token' => $checkout->token,
204
221
  ]);
205
222
  }
206
223
 
207
- public static function initializeQpayCheckout(Contact $customer, Gateway $gateway, ServiceQuote $serviceQuote, Cart $cart, $checkoutOptions)
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
+ if (!$serviceQuote) {
331
+ return response()->apiError('Invalid service quote ID provided');
332
+ }
333
+
334
+ // Recalculate amount based on cart, serviceQuote, and checkoutOptions
335
+ $amount = static::calculateCheckoutAmount($cart, $serviceQuote, $checkoutOptions);
336
+ $currency = $cart->getCurrency();
337
+
338
+ // Check for Stripe secret key
339
+ if (!isset($gateway->config->secret_key)) {
340
+ return response()->apiError('Gateway not configured correctly!');
341
+ }
342
+
343
+ // Set Stripe API key
344
+ \Stripe\Stripe::setApiKey($gateway->config->secret_key);
345
+
346
+ // Ensure customer has a stripe_id
347
+ if ($customer->missingMeta('stripe_id')) {
348
+ Storefront::createStripeCustomerForContact($customer);
349
+ }
350
+
351
+ // Retrieve the existing PaymentIntent
352
+ try {
353
+ $paymentIntent = \Stripe\PaymentIntent::retrieve($paymentIntentId);
354
+ } catch (\Exception $e) {
355
+ return response()->apiError('Failed to retrieve PaymentIntent: ' . $e->getMessage());
356
+ }
357
+
358
+ // Check if PaymentIntent is in a modifiable state
359
+ $modifiableStatuses = ['requires_payment_method', 'requires_confirmation', 'requires_action', 'processing'];
360
+ if (!in_array($paymentIntent->status, $modifiableStatuses)) {
361
+ return response()->apiError('PaymentIntent cannot be updated at this stage.');
362
+ }
363
+
364
+ // Prepare the updated data
365
+ $updateData = [
366
+ 'amount' => Utils::formatAmountForStripe($amount, $currency),
367
+ 'currency' => $currency,
368
+ ];
369
+
370
+ // Update the PaymentIntent
371
+ try {
372
+ $paymentIntent = \Stripe\PaymentIntent::update($paymentIntentId, $updateData);
373
+ } catch (\Exception $e) {
374
+ return response()->apiError('Failed to update PaymentIntent: ' . $e->getMessage());
375
+ }
376
+
377
+ // If payment intent has a payment method set already update for the customer
378
+ $paymentIntentPaymentMethodId = $paymentIntent->payment_method;
379
+ $customerPaymentMethodId = $customer->getMeta('stripe_payment_method_id');
380
+ if ($paymentIntentPaymentMethodId !== $customerPaymentMethodId) {
381
+ $customer->updateMeta('stripe_payment_method_id', $paymentIntentPaymentMethodId);
382
+ }
383
+
384
+ // Create a new EphemeralKey if needed for the frontend
385
+ try {
386
+ $ephemeralKey = \Stripe\EphemeralKey::create(
387
+ ['customer' => $customer->getMeta('stripe_id')],
388
+ ['stripe_version' => '2020-08-27']
389
+ );
390
+ } catch (\Exception $e) {
391
+ return response()->apiError('Failed to create ephemeral key: ' . $e->getMessage());
392
+ }
393
+
394
+ // Create a new checkout token
395
+ $checkout = Checkout::create([
396
+ 'company_uuid' => session('company'),
397
+ 'store_uuid' => session('storefront_store'),
398
+ 'network_uuid' => session('storefront_network'),
399
+ 'cart_uuid' => $cart->uuid,
400
+ 'gateway_uuid' => $gateway->uuid,
401
+ 'service_quote_uuid' => $serviceQuote->uuid,
402
+ 'owner_uuid' => $customer->uuid,
403
+ 'owner_type' => 'fleet-ops:contact',
404
+ 'amount' => $amount,
405
+ 'currency' => $currency,
406
+ 'is_pickup' => $isPickup,
407
+ 'options' => $checkoutOptions,
408
+ 'cart_state' => $cart->toArray(),
409
+ ]);
410
+
411
+ // Return JSON response with updated PaymentIntent and ephemeral key
412
+ return response()->json([
413
+ 'paymentIntent' => $paymentIntent->id,
414
+ 'clientSecret' => $paymentIntent->client_secret,
415
+ 'ephemeralKey' => $ephemeralKey->secret,
416
+ 'customerId' => $customer->getMeta('stripe_id'),
417
+ 'token' => $checkout->token,
418
+ ]);
419
+ }
420
+
421
+ public static function initializeQPayCheckout(Contact $customer, Gateway $gateway, ServiceQuote $serviceQuote, Cart $cart, $checkoutOptions)
208
422
  {
209
423
  // Get store info
210
424
  $about = Storefront::about();
@@ -214,11 +428,11 @@ class CheckoutController extends Controller
214
428
 
215
429
  // get amount/subtotal
216
430
  $amount = static::calculateCheckoutAmount($cart, $serviceQuote, $checkoutOptions);
217
- $currency = $cart->currency ?? session('storefront_currency');
431
+ $currency = $cart->getCurrency();
218
432
 
219
433
  // check for secret key first
220
434
  if (!isset($gateway->config->username)) {
221
- return response()->error('Gateway not configured correctly!');
435
+ return response()->apiError('Gateway not configured correctly!');
222
436
  }
223
437
 
224
438
  // Create qpay instance
@@ -231,15 +445,8 @@ class CheckoutController extends Controller
231
445
  // Set auth token
232
446
  $qpay = $qpay->setAuthToken();
233
447
 
234
- // Create invoice description
235
- $invoiceAmount = round($amount / 100);
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);
448
+ // Test payment
449
+ $testPayment = is_string(data_get($checkoutOptions, 'testPayment')) && $gateway->sandbox;
243
450
 
244
451
  // Create checkout token
245
452
  $checkout = Checkout::create([
@@ -258,12 +465,103 @@ class CheckoutController extends Controller
258
465
  'cart_state' => $cart->toArray(),
259
466
  ]);
260
467
 
468
+ // Set QPay Callback
469
+ $callbackParams = ['checkout' => $checkout->public_id];
470
+ if ($testPayment) {
471
+ $callbackParams['test'] = data_get($checkoutOptions, 'testPayment');
472
+ }
473
+ $callbackUrl = Utils::apiUrl('storefront/v1/checkouts/capture-qpay', $callbackParams);
474
+
475
+ // Create invoice description
476
+ // $invoiceAmount = round($amount / 100);
477
+ $invoiceAmount = $amount;
478
+ $invoiceCode = $gateway->sandbox ? 'TEST_INVOICE' : $gateway->config->invoice_id;
479
+ $invoiceDescription = $about->name . ' cart checkout';
480
+ $invoiceReceiverCode = $gateway->public_id;
481
+ $senderInvoiceNo = $cart->id;
482
+
483
+ // Create qpay invoice
484
+ $invoice = $qpay->createSimpleInvoice($invoiceAmount, $invoiceCode, $invoiceDescription, $invoiceReceiverCode, $senderInvoiceNo, $callbackUrl);
485
+
486
+ // Update checkout with invoice id
487
+ $checkout->updateOption('qpay_invoice_id', data_get($invoice, 'invoice_id'));
488
+
261
489
  return response()->json([
262
- 'invoice' => $invoice,
263
- 'token' => $checkout->token,
490
+ 'invoice' => $invoice,
491
+ 'checkout' => $checkout->public_id,
492
+ 'token' => $checkout->token,
264
493
  ]);
265
494
  }
266
495
 
496
+ public function captureQPayCallback(Request $request)
497
+ {
498
+ $checkoutId = $request->input('checkout');
499
+ $respond = $request->boolean('respond');
500
+ $test = $request->input('test'); // success, error
501
+
502
+ if ($checkoutId) {
503
+ // Get the checkout instance
504
+ $checkout = Checkout::where('public_id', $checkoutId)->first();
505
+ if ($checkout) {
506
+ // Restore the payment gateway instance
507
+ $gateway = Gateway::where('uuid', $checkout->gateway_uuid)->first();
508
+ if ($gateway) {
509
+ try {
510
+ // If testing send back test data
511
+ // Handle test below
512
+ if ($gateway->sandbox && is_string($test) && in_array($test, ['success', 'error'])) {
513
+ $data = ['error' => null, 'checkout' => $checkout->public_id, 'payment' => null];
514
+ if ($test === 'success') {
515
+ $data['payment'] = QPay::createTestPaymentDataFromCheckout($checkout);
516
+ } else {
517
+ $data['error'] = ['error' => 'PAYMENT_NOT_PAID', 'message' => 'Payment has not paid!'];
518
+ }
519
+
520
+ SocketClusterService::publish('checkout.' . $checkout->public_id, $data);
521
+ if ($respond) {
522
+ return response()->json($data);
523
+ }
524
+
525
+ return;
526
+ }
527
+
528
+ // Create qpay instance
529
+ $qpay = QPay::instance($gateway->config->username, $gateway->config->password, $gateway->callback_url);
530
+ if ($gateway->sandbox) {
531
+ $qpay = $qpay->useSandbox();
532
+ }
533
+
534
+ // Set auth token
535
+ $qpay = $qpay->setAuthToken();
536
+
537
+ // Check payment status from qpay
538
+ $payment = $qpay->paymentGet($checkoutId);
539
+ if ($payment) {
540
+ $data = [];
541
+ if ($payment->error) {
542
+ $data = ['error' => (array) $payment, 'checkout' => $checkout->public_id, 'payment' => null];
543
+ } else {
544
+ $data = ['payment' =>(array) $payment, 'checkout' => $checkout->public_id, 'error' => null];
545
+ }
546
+
547
+ SocketClusterService::publish('checkout.' . $checkout->public_id, $data);
548
+ if ($respond) {
549
+ return response()->json($data);
550
+ }
551
+ }
552
+ } catch (\Exception $e) {
553
+ Log::error('[QPAY CHECKOUT ERROR: ' . $e->getMessage() . ']', $checkout->toArray());
554
+ if ($respond) {
555
+ return response()->apiError($e->getMessage());
556
+ }
557
+ }
558
+ }
559
+ }
560
+ }
561
+
562
+ return response()->json();
563
+ }
564
+
267
565
  /**
268
566
  * Process a cart item and create/save an entity.
269
567
  *
@@ -327,7 +625,7 @@ class CheckoutController extends Controller
327
625
 
328
626
  // $amount = $checkout->amount ?? ($checkout->is_pickup ? $cart->subtotal : $cart->subtotal + $serviceQuote->amount);
329
627
  $amount = static::calculateCheckoutAmount($cart, $serviceQuote, $checkout->options);
330
- $currency = $checkout->currency ?? ($cart->currency ?? session('storefront_currency'));
628
+ $currency = $checkout->currency ?? $cart->getCurrency();
331
629
  $store = $about;
332
630
 
333
631
  // check if order is via network for a single store
@@ -348,7 +646,7 @@ class CheckoutController extends Controller
348
646
 
349
647
  // super rare condition
350
648
  if (!$store) {
351
- return response()->error('No storefront in request to capture order!');
649
+ return response()->apiError('No storefront in request to capture order!');
352
650
  }
353
651
 
354
652
  // prepare for integrated vendor order if applicable
@@ -360,7 +658,7 @@ class CheckoutController extends Controller
360
658
  try {
361
659
  $integratedVendorOrder = $serviceQuote->integratedVendor->api()->createOrderFromServiceQuote($serviceQuote, $request);
362
660
  } catch (\Exception $e) {
363
- return response()->error($e->getMessage());
661
+ return response()->apiError($e->getMessage());
364
662
  }
365
663
  }
366
664
 
@@ -601,10 +899,10 @@ class CheckoutController extends Controller
601
899
  $cart = $checkout->cart;
602
900
  // $amount = $checkout->amount ?? ($checkout->is_pickup ? $cart->subtotal : $cart->subtotal + $serviceQuote->amount);
603
901
  $amount = static::calculateCheckoutAmount($cart, $serviceQuote, $checkout->options);
604
- $currency = $checkout->currency ?? ($cart->currency ?? session('storefront_currency'));
902
+ $currency = $checkout->currency ?? $cart->getCurrency();
605
903
 
606
904
  if (!$about) {
607
- return response()->error('No network in request to capture order!');
905
+ return response()->apiError('No network in request to capture order!');
608
906
  }
609
907
 
610
908
  // prepare for integrated vendor order if applicable
@@ -616,7 +914,7 @@ class CheckoutController extends Controller
616
914
  try {
617
915
  $integratedVendorOrder = $serviceQuote->integratedVendor->api()->createOrderFromServiceQuote($serviceQuote, $request);
618
916
  } catch (\Exception $e) {
619
- return response()->error($e->getMessage());
917
+ return response()->apiError($e->getMessage());
620
918
  }
621
919
  }
622
920
 
@@ -251,6 +251,14 @@ class CustomerController extends Controller
251
251
  // always customer type
252
252
  $input['type'] = 'customer';
253
253
 
254
+ // If setting a default location for the contact
255
+ if ($request->has('place')) {
256
+ $input['place_uuid'] = Utils::getUuid('places', [
257
+ 'public_id' => $request->input('place'),
258
+ 'company_uuid' => session('company'),
259
+ ]);
260
+ }
261
+
254
262
  // update the contact
255
263
  $contact->update($input);
256
264
 
@@ -474,4 +482,73 @@ class CustomerController extends Controller
474
482
 
475
483
  return $phone;
476
484
  }
485
+
486
+ public function getStripeEphemeralKey(Request $request)
487
+ {
488
+ $customer = Storefront::getCustomerFromToken();
489
+ if (!$customer) {
490
+ return response()->error('Not authorized to view customers places');
491
+ }
492
+
493
+ $gateway = Storefront::findGateway('stripe');
494
+ if (!$gateway) {
495
+ return response()->apiError('Stripe not setup.');
496
+ }
497
+
498
+ \Stripe\Stripe::setApiKey($gateway->config->secret_key);
499
+
500
+ // Ensure customer has a stripe_id
501
+ if ($customer->missingMeta('stripe_id')) {
502
+ Storefront::createStripeCustomerForContact($customer);
503
+ }
504
+
505
+ try {
506
+ // Create Ephemeral Key
507
+ $ephemeralKey = \Stripe\EphemeralKey::create(
508
+ ['customer' => $customer->getMeta('stripe_id')],
509
+ ['stripe_version' => '2020-08-27']
510
+ );
511
+
512
+ return response()->json([
513
+ 'ephemeralKey' => $ephemeralKey->secret,
514
+ 'customer' => $customer->getMeta('stripe_id'),
515
+ ]);
516
+ } catch (\Exception $e) {
517
+ return response()->apiError($e->getMessage());
518
+ }
519
+ }
520
+
521
+ public function getStripeSetupIntent(Request $request)
522
+ {
523
+ $customer = Storefront::getCustomerFromToken();
524
+ if (!$customer) {
525
+ return response()->error('Not authorized to view customers places');
526
+ }
527
+
528
+ $gateway = Storefront::findGateway('stripe');
529
+ if (!$gateway) {
530
+ return response()->apiError('Stripe not setup.');
531
+ }
532
+
533
+ \Stripe\Stripe::setApiKey($gateway->config->secret_key);
534
+
535
+ // Ensure customer has a stripe_id
536
+ if ($customer->missingMeta('stripe_id')) {
537
+ Storefront::createStripeCustomerForContact($customer);
538
+ }
539
+
540
+ try {
541
+ // Create SetupIntent
542
+ $setupIntent = \Stripe\SetupIntent::create([
543
+ 'customer' => $customer->getMeta('stripe_id'),
544
+ ]);
545
+
546
+ return response()->json([
547
+ 'setupIntentId' => $setupIntent->id,
548
+ 'setupIntent' => $setupIntent->client_secret,
549
+ ]);
550
+ } catch (\Exception $e) {
551
+ return response()->apiError($e->getMessage());
552
+ }
553
+ }
477
554
  }
@@ -4,11 +4,12 @@ namespace Fleetbase\Storefront\Http\Controllers\v1;
4
4
 
5
5
  use Fleetbase\Http\Controllers\Controller;
6
6
  use Fleetbase\Storefront\Http\Resources\Gateway as GatewayResource;
7
- use Fleetbase\Storefront\Http\Resources\Network;
7
+ use Fleetbase\Storefront\Http\Resources\Network as NetworkResource;
8
8
  use Fleetbase\Storefront\Http\Resources\Product as ProductResource;
9
- use Fleetbase\Storefront\Http\Resources\Store as StorefrontStore;
9
+ use Fleetbase\Storefront\Http\Resources\Store as StorefrontResource;
10
10
  use Fleetbase\Storefront\Http\Resources\StoreLocation as StoreLocationResource;
11
11
  use Fleetbase\Storefront\Models\Gateway;
12
+ use Fleetbase\Storefront\Models\Network;
12
13
  use Fleetbase\Storefront\Models\Product;
13
14
  use Fleetbase\Storefront\Models\Store;
14
15
  use Fleetbase\Storefront\Models\StoreLocation;
@@ -32,10 +33,34 @@ class StoreController extends Controller
32
33
  }
33
34
 
34
35
  if ($about->is_store) {
35
- return new StorefrontStore($about);
36
+ return new StorefrontResource($about);
36
37
  }
37
38
 
38
- return new Network($about);
39
+ return new NetworkResource($about);
40
+ }
41
+
42
+ /**
43
+ * Lookup a store or network provided the ID.
44
+ *
45
+ * @return \Illuminate\Http\Response
46
+ */
47
+ public function lookup(?string $id)
48
+ {
49
+ if (!$id) {
50
+ return response()->apiError('No ID provided for lookup.');
51
+ }
52
+
53
+ $store = Store::where(['public_id' => $id, 'company_uuid' => session('company')])->first();
54
+ if ($store) {
55
+ return new StorefrontResource($store);
56
+ }
57
+
58
+ $network = Network::where(['public_id' => $id, 'company_uuid' => session('company')])->first();
59
+ if ($network) {
60
+ return new NetworkResource($network);
61
+ }
62
+
63
+ return response()->apiError('Unable to find store or network for ID provided.');
39
64
  }
40
65
 
41
66
  /**
@@ -125,7 +150,12 @@ class StoreController extends Controller
125
150
  $ownerId = session('storefront_store') ?? session('storefront_network');
126
151
 
127
152
  $sandbox = $request->input('sandbox', false);
128
- $query = Gateway::select(['public_id', 'name', 'code', 'type', 'sandbox', 'return_url', 'callback_url'])->where(['public_id' => $id, 'owner_uuid' => $ownerId]);
153
+ $query = Gateway::select(['public_id', 'name', 'code', 'type', 'sandbox', 'return_url', 'callback_url'])
154
+ ->where(['owner_uuid' => $ownerId])
155
+ ->where(function ($q) use ($id) {
156
+ $q->where('public_id', $id);
157
+ $q->orWhere('code', $id);
158
+ });
129
159
 
130
160
  if ($sandbox) {
131
161
  $query->where('sandbox', 1);