@fleetbase/storefront-engine 0.3.16 → 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.
- package/addon/components/modals/manage-addons.js +6 -2
- 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/models/product-addon-category.js +3 -0
- package/addon/styles/storefront-engine.css +7 -0
- package/addon/templates/products/index/category/new.hbs +4 -1
- package/addon/templates/settings/gateways.hbs +1 -1
- package/addon/templates/settings/index.hbs +2 -2
- package/composer.json +1 -7
- package/extension.json +1 -1
- package/package.json +4 -4
- package/server/src/Http/Controllers/ProductController.php +2 -0
- package/server/src/Http/Controllers/v1/CheckoutController.php +331 -33
- package/server/src/Http/Controllers/v1/CustomerController.php +77 -0
- package/server/src/Http/Controllers/v1/StoreController.php +35 -5
- package/server/src/Http/Requests/CreateStripeSetupIntentRequest.php +31 -0
- package/server/src/Http/Requests/CustomerRequest.php +31 -0
- 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/Support/QPay.php +35 -1
- package/server/src/Support/StripeUtils.php +38 -0
- package/server/src/routes.php +19 -0
- 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(['
|
|
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');
|
|
@@ -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->
|
|
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([
|
|
@@ -197,14 +213,212 @@ class CheckoutController extends Controller
|
|
|
197
213
|
]);
|
|
198
214
|
|
|
199
215
|
return response()->json([
|
|
200
|
-
'paymentIntent' => $paymentIntent->
|
|
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
|
|
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->
|
|
431
|
+
$currency = $cart->getCurrency();
|
|
218
432
|
|
|
219
433
|
// check for secret key first
|
|
220
434
|
if (!isset($gateway->config->username)) {
|
|
221
|
-
return response()->
|
|
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
|
-
//
|
|
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);
|
|
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'
|
|
263
|
-
'
|
|
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 ??
|
|
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()->
|
|
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()->
|
|
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 ??
|
|
902
|
+
$currency = $checkout->currency ?? $cart->getCurrency();
|
|
605
903
|
|
|
606
904
|
if (!$about) {
|
|
607
|
-
return response()->
|
|
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()->
|
|
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
|
|
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
|
|
36
|
+
return new StorefrontResource($about);
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
return new
|
|
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'])
|
|
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);
|