34 | {paymentGateways.map(({ id, name, config }, index) => {
35 | const checked = selectedPaymentGateway === id;
36 |
37 | switch (name) {
38 | case PROVIDERS.BRAINTREE.label:
39 | return (
40 |
41 |
42 |
48 | selectPaymentGateway && selectPaymentGateway(id)
49 | }
50 | customLabel
51 | >
52 |
53 | {name}
54 |
55 |
56 |
57 | {checked && (
58 |
63 | processPayment(id, token, cardData)
64 | }
65 | errors={errors}
66 | onError={onError}
67 | />
68 | )}
69 |
70 | );
71 |
72 | case PROVIDERS.DUMMY.label:
73 | return (
74 |
75 |
76 |
82 | selectPaymentGateway && selectPaymentGateway(id)
83 | }
84 | customLabel
85 | >
86 |
87 | {name}
88 |
89 |
90 |
91 | {checked && (
92 | processPayment(id, token)}
96 | initialStatus={selectedPaymentGatewayToken}
97 | />
98 | )}
99 |
100 | );
101 |
102 | case PROVIDERS.STRIPE.label:
103 | return (
104 |
105 |
106 |
112 | selectPaymentGateway && selectPaymentGateway(id)
113 | }
114 | customLabel
115 | >
116 |
117 | {name}
118 |
119 |
120 |
121 | {checked && (
122 |
127 | processPayment(id, token, cardData)
128 | }
129 | errors={errors}
130 | onError={onError}
131 | />
132 | )}
133 |
134 | );
135 |
136 | case PROVIDERS.ADYEN.label:
137 | return (
138 |
139 |
140 |
146 | selectPaymentGateway && selectPaymentGateway(id)
147 | }
148 | customLabel
149 | >
150 |
151 | {name}
152 |
153 |
154 |
155 | {checked && (
156 |
processPayment(id)}
162 | submitPayment={submitPayment}
163 | submitPaymentSuccess={submitPaymentSuccess}
164 | errors={errors}
165 | onError={onError}
166 | />
167 | )}
168 |
169 | );
170 | case PROVIDERS.SBERBANK.label:
171 | return (
172 |
173 |
174 |
180 | selectPaymentGateway && selectPaymentGateway(id)
181 | }
182 | >
183 |
184 | Банковская карта
185 |
186 |
187 |
188 | {checked && (
189 | processPayment(id)}
192 | submitPayment={submitPayment}
193 | submitPaymentSuccess={submitPaymentSuccess}
194 | errors={errors}
195 | onError={onError}
196 | />
197 | )}
198 |
199 | );
200 |
201 | default:
202 | return null;
203 | }
204 | })}
205 | {!selectedPaymentGateway && errors && }
206 |
207 | );
208 | };
209 |
210 | export { PaymentGatewaysList };
211 |
--------------------------------------------------------------------------------
/saleor/payment/gateways/sberbank/__init__.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from urllib.parse import urlencode
3 |
4 | from django.core.exceptions import ObjectDoesNotExist
5 |
6 | from ....core.utils import build_absolute_uri
7 | from ....core.utils.url import prepare_url
8 | from ... import PaymentError
9 | from ...interface import (
10 | GatewayConfig,
11 | GatewayResponse,
12 | PaymentData,
13 | )
14 |
15 | from ...utils import create_transaction, TransactionKind
16 |
17 | from ...models import Payment
18 |
19 | from .forms import SberbankPaymentForm
20 | from . import errors
21 | from .utils import (
22 | get_amount_for_sberbank,
23 | get_error_response,
24 | get_return_url,
25 | get_data_for_payment)
26 |
27 | from . import client as sberbank
28 |
29 | from .tasks import check_status_sberbank_task
30 |
31 | # The list of currencies supported by razorpay
32 | SUPPORTED_CURRENCIES = ("RUB", "USD")
33 | PENDING_STATUSES = [""]
34 |
35 | # Define what are the Sberbank exceptions,
36 | # as the Sberbank provider doesn't define a base exception as of now.
37 | SBERBANK_EXCEPTIONS = (
38 | sberbank.errors.BadRequestError,
39 | sberbank.errors.GatewayError,
40 | sberbank.errors.ServerError,
41 | )
42 |
43 | # Get the logger for this file, it will allow us to log
44 | # error responses from Sberbank.
45 | logger = logging.getLogger(__name__)
46 |
47 |
48 | def get_error_message_from_sberbank_error(exc: BaseException):
49 | """Convert a Razorpay error to a user-friendly error message.
50 |
51 | It also logs the exception to stderr.
52 | """
53 | logger.exception(exc)
54 | if isinstance(exc, sberbank.errors.BadRequestError):
55 | return errors.INVALID_REQUEST
56 | else:
57 | return errors.SERVER_ERROR
58 |
59 |
60 | def check_payment_supported(payment_information: PaymentData):
61 | """Check that a given payment is supported."""
62 | if payment_information.currency not in SUPPORTED_CURRENCIES:
63 | return errors.UNSUPPORTED_CURRENCY % {"currency": payment_information.currency}
64 |
65 |
66 | def get_client(connection_params):
67 | """Create a Sberbank client from set-up application keys."""
68 | sberbank_client = sberbank.Client(
69 | auth=(connection_params['login'], connection_params['password']),
70 | sandbox=connection_params['sandbox_mode'])
71 | return sberbank_client
72 |
73 |
74 | def process_payment(self, payment_information: PaymentData, config: GatewayConfig
75 | ) -> GatewayResponse:
76 | # return authorize(payment_information, config)
77 |
78 | try:
79 | payment = Payment.objects.get(pk=payment_information.payment_id)
80 | except ObjectDoesNotExist:
81 | raise PaymentError("Payment cannot be performed. Payment does not exists.")
82 |
83 | checkout = payment.checkout
84 | if checkout is None:
85 | raise PaymentError(
86 | "Payment cannot be performed. Checkout for this payment does not exist."
87 | )
88 |
89 | params = urlencode(
90 | {"payment": payment_information.graphql_payment_id, "checkout": checkout.pk}
91 | )
92 | return_url = prepare_url(
93 | params,
94 | build_absolute_uri(
95 | f"/plugins/{self.PLUGIN_ID}/additional-actions"
96 | ), # type: ignore
97 | )
98 |
99 | error = check_payment_supported(payment_information=payment_information)
100 |
101 | sberbank_client = get_client(config.connection_params)
102 |
103 | try:
104 |
105 | kind = TransactionKind.AUTH
106 | sberbank_auto_capture = self.config.auto_capture
107 | if sberbank_auto_capture:
108 | kind = TransactionKind.CAPTURE
109 |
110 | response = sberbank_client.payment.register(
111 | order_id=payment_information.payment_id,
112 | amount=get_amount_for_sberbank(payment_information.amount),
113 | return_url=return_url,
114 | data=get_data_for_payment(payment_information))
115 | # response = {"formUrl": "https://3dsec.sberbank.ru/payment/merchants/sbersafe_id/payment_ru.html?mdOrder=389320f5-d423-714b-bae5-ca325e3d5a10",
116 | # "orderId": "389320f5-d423-714b-bae5-ca325e3d5a10"}
117 |
118 | # orderId есть только у успешно зарегистрированных заказов
119 | if 'orderId' in response:
120 | token = response['orderId']
121 | payment_information.token = token
122 |
123 | # Запустим проверку оплаты с API-сбербанка
124 | # check_status_sberbank_task.delay(order_id=token,
125 | # connection_params=config.connection_params)
126 |
127 | action = {
128 | 'method': 'GET',
129 | 'type': 'redirect',
130 | 'paymentMethodType': 'sberbank',
131 | 'paymentData': token,
132 | 'url': response['formUrl']
133 | }
134 |
135 | return GatewayResponse(
136 | is_success=True,
137 | action_required=True,
138 | transaction_id=token,
139 | amount=payment_information.amount,
140 | currency=payment_information.currency,
141 | kind=kind,
142 | error='',
143 | raw_response=response,
144 | action_required_data=action,
145 | customer_id=payment_information.customer_id,
146 | searchable_key=token,
147 | )
148 |
149 | if 'errorCode' in response:
150 | error_code = int(response['errorCode'])
151 | if error_code in errors.ERRORS:
152 | error_msg = response['errorMessage']
153 | logger.critical('{}:{}'.format(error_code, error_msg))
154 |
155 | return GatewayResponse(
156 | is_success=False,
157 | action_required=True,
158 | amount=payment_information.amount,
159 | error=error_msg,
160 | transaction_id='',
161 | currency=payment_information.currency,
162 | kind=kind,
163 | raw_response=response,
164 | customer_id=payment_information.customer_id,
165 | )
166 | # raise Exception(error_msg)
167 |
168 | except SBERBANK_EXCEPTIONS as exc:
169 | error = get_error_message_from_sberbank_error(exc)
170 |
171 | return GatewayResponse(
172 | is_success=False,
173 | action_required=False,
174 | currency=payment_information.currency,
175 | error=error,
176 | customer_id=payment_information.customer_id,
177 | )
178 |
179 |
180 | def confirm_payment(
181 | self, payment_information: "PaymentData", previous_value
182 | ) -> "GatewayResponse":
183 | config = self._get_gateway_config()
184 | # The additional checks are proceed asynchronously so we try to confirm that
185 | # the payment is already processed
186 | payment = Payment.objects.filter(id=payment_information.payment_id).first()
187 | if not payment:
188 | raise PaymentError("Unable to find the payment.")
189 |
190 | transaction = (
191 | payment.transactions.filter(
192 | kind=TransactionKind.ACTION_TO_CONFIRM,
193 | is_success=True,
194 | action_required=False,
195 | )
196 | .exclude(token__isnull=False, token__exact="")
197 | .last()
198 | )
199 |
200 | kind = TransactionKind.AUTH
201 | if config.auto_capture:
202 | kind = TransactionKind.CAPTURE
203 |
204 | # if not transaction:
205 | # return self._process_additional_action(payment_information, kind)
206 |
207 | result_code = transaction.gateway_response.get("actionCodeDescription", "").strip().lower()
208 | if result_code and result_code in PENDING_STATUSES:
209 | kind = TransactionKind.PENDING
210 |
211 | transaction_already_processed = payment.transactions.filter(
212 | kind=kind,
213 | is_success=True,
214 | action_required=False,
215 | amount=payment_information.amount,
216 | currency=payment_information.currency,
217 | ).first()
218 | is_success = True
219 |
220 | # confirm that we should proceed the capture action
221 | if (
222 | not transaction_already_processed
223 | and config.auto_capture
224 | and kind == TransactionKind.CAPTURE
225 | ):
226 | is_success = True
227 |
228 | token = transaction.token
229 | if transaction_already_processed:
230 | token = transaction_already_processed.token
231 |
232 | return GatewayResponse(
233 | is_success=is_success,
234 | action_required=False,
235 | kind=kind,
236 | amount=payment_information.amount, # type: ignore
237 | currency=payment_information.currency, # type: ignore
238 | transaction_id=token, # type: ignore
239 | error=None,
240 | raw_response={},
241 | transaction_already_processed=bool(transaction_already_processed),
242 | )
243 |
--------------------------------------------------------------------------------
/saleor/payment/gateways/sberbank/webhooks.py:
--------------------------------------------------------------------------------
1 | import base64
2 | import binascii
3 | import hashlib
4 | import hmac
5 | import json
6 | import logging
7 | from typing import Any, Callable, Dict, Optional
8 | from urllib.parse import urlencode
9 |
10 | import Adyen
11 | import graphene
12 | from django.contrib.auth.hashers import check_password
13 | from django.contrib.auth.models import AnonymousUser
14 | from django.core.exceptions import ValidationError
15 | from django.core.handlers.wsgi import WSGIRequest
16 | from django.http import (
17 | HttpResponse,
18 | HttpResponseBadRequest,
19 | HttpResponseNotFound,
20 | QueryDict,
21 | )
22 | from django.http.request import HttpHeaders
23 | from django.shortcuts import redirect
24 | from graphql_relay import from_global_id
25 |
26 | from ....checkout.complete_checkout import complete_checkout
27 | from ....checkout.models import Checkout
28 | from ....core.transactions import transaction_with_commit_on_errors
29 | from ....core.utils.url import prepare_url
30 | from ....discount.utils import fetch_active_discounts
31 | from ....order.actions import (
32 | cancel_order,
33 | order_authorized,
34 | order_captured,
35 | order_refunded,
36 | )
37 | from ....order.events import external_notification_event
38 | from ....payment.models import Payment, Transaction
39 | from ... import ChargeStatus, PaymentError, TransactionKind
40 | from ...gateway import payment_refund_or_void
41 | from ...interface import GatewayConfig, GatewayResponse
42 | from ...utils import create_payment_information, create_transaction, gateway_postprocess
43 | from .utils import api_call
44 | from .errors import ERRORS as FAILED_STATUSES
45 |
46 | logger = logging.getLogger(__name__)
47 |
48 |
49 | def get_payment(
50 | payment_id: Optional[str], transaction_id: Optional[str] = None
51 | ) -> Optional[Payment]:
52 | transaction_id = transaction_id or ""
53 | if not payment_id:
54 | logger.warning("Missing payment ID. Reference %s", transaction_id)
55 | return None
56 | try:
57 | _type, db_payment_id = from_global_id(payment_id)
58 | except UnicodeDecodeError:
59 | logger.warning(
60 | "Unable to decode the payment ID %s. Reference %s",
61 | payment_id,
62 | transaction_id,
63 | )
64 | return None
65 | payment = (
66 | Payment.objects.prefetch_related("order", "checkout")
67 | .select_for_update(of=("self",))
68 | .filter(id=db_payment_id, is_active=True, gateway="korolev.payments.sberbank")
69 | .first()
70 | )
71 | if not payment:
72 | logger.warning(
73 | "Payment for %s was not found. Reference %s", payment_id, transaction_id
74 | )
75 | return payment
76 |
77 |
78 | def get_checkout(payment: Payment) -> Optional[Checkout]:
79 | if not payment.checkout:
80 | return None
81 | # Lock checkout in the same way as in checkoutComplete
82 | return (
83 | Checkout.objects.select_for_update(of=("self",))
84 | .prefetch_related("gift_cards", "lines__variant__product", )
85 | .select_related("shipping_method__shipping_zone")
86 | .filter(pk=payment.checkout.pk)
87 | .first()
88 | )
89 |
90 |
91 | def get_transaction(
92 | payment: "Payment", transaction_id: Optional[str], kind: str,
93 | ) -> Optional[Transaction]:
94 | transaction = payment.transactions.filter(kind=kind, token=transaction_id).last()
95 | return transaction
96 |
97 |
98 | def create_new_transaction(notification, payment, kind):
99 | transaction_id = notification.get("pspReference")
100 | currency = notification.get("amount", {}).get("currency")
101 | # amount = from_sberbank_price(notification.get("amount", {}).get("value"))
102 | amount = notification.get("amount", {}).get("value")
103 | is_success = True if notification.get("success") == "true" else False
104 |
105 | gateway_response = GatewayResponse(
106 | kind=kind,
107 | action_required=False,
108 | transaction_id=transaction_id,
109 | is_success=is_success,
110 | amount=amount,
111 | currency=currency,
112 | error="",
113 | raw_response={},
114 | searchable_key=transaction_id,
115 | )
116 | return create_transaction(
117 | payment,
118 | kind=kind,
119 | payment_information=None,
120 | action_required=False,
121 | gateway_response=gateway_response,
122 | )
123 |
124 |
125 | def create_payment_notification_for_order(
126 | payment: Payment, success_msg: str, failed_msg: Optional[str], is_success: bool
127 | ):
128 | if not payment.order:
129 | # Order is not assigned
130 | return
131 | msg = success_msg if is_success else failed_msg
132 |
133 | external_notification_event(
134 | order=payment.order,
135 | user=None,
136 | message=msg,
137 | parameters={"service": payment.gateway, "id": payment.token},
138 | )
139 |
140 |
141 | def create_order(payment, checkout):
142 | try:
143 | discounts = fetch_active_discounts()
144 | order, _, _ = complete_checkout(
145 | checkout=checkout,
146 | payment_data={},
147 | store_source=False,
148 | discounts=discounts,
149 | user=checkout.user or AnonymousUser(),
150 | )
151 | except ValidationError:
152 | payment_refund_or_void(payment)
153 | return None
154 | # Refresh the payment to assign the newly created order
155 | payment.refresh_from_db()
156 | return order
157 |
158 |
159 | def handle_not_created_order(notification, payment, checkout):
160 | """Process the notification in case when payment doesn't have assigned order."""
161 |
162 | # We don't want to create order for payment that is cancelled or refunded
163 | if payment.charge_status not in {
164 | ChargeStatus.NOT_CHARGED,
165 | ChargeStatus.PENDING,
166 | ChargeStatus.PARTIALLY_CHARGED,
167 | ChargeStatus.FULLY_CHARGED,
168 | }:
169 | return
170 | # If the payment is not Auth/Capture, it means that user didn't return to the
171 | # storefront and we need to finalize the checkout asynchronously.
172 | action_transaction = create_new_transaction(
173 | notification, payment, TransactionKind.ACTION_TO_CONFIRM
174 | )
175 |
176 | # Only when we confirm that notification is success we will create the order
177 | if action_transaction.is_success and checkout: # type: ignore
178 | order = create_order(payment, checkout)
179 | return order
180 | return None
181 |
182 |
183 | def handle_authorization(notification: Dict[str, Any], gateway_config: GatewayConfig):
184 | # TODO: handle_authorization
185 | pass
186 |
187 |
188 | def handle_cancellation(notification: Dict[str, Any], _gateway_config: GatewayConfig):
189 | # TODO: handle_cancellation
190 | pass
191 |
192 |
193 | def handle_cancel_or_refund(
194 | notification: Dict[str, Any], gateway_config: GatewayConfig
195 | ):
196 | # TODO: handle_cancel_or_refund
197 | pass
198 |
199 |
200 | def handle_capture(notification: Dict[str, Any], _gateway_config: GatewayConfig):
201 | # TODO: handle_capture
202 | pass
203 |
204 |
205 | def handle_failed_capture(notification: Dict[str, Any], _gateway_config: GatewayConfig):
206 | # TODO: handle_failed_capture
207 | pass
208 |
209 |
210 | def handle_pending(notification: Dict[str, Any], gateway_config: GatewayConfig):
211 | # TODO: handle_pending
212 | pass
213 |
214 |
215 | def handle_refund(notification: Dict[str, Any], _gateway_config: GatewayConfig):
216 | # TODO: handle_refund
217 | pass
218 |
219 |
220 | def _get_kind(transaction: Optional[Transaction]) -> str:
221 | if transaction:
222 | return transaction.kind
223 | # To proceed the refund we already need to have the capture status so we will use it
224 | return TransactionKind.CAPTURE
225 |
226 |
227 | def handle_failed_refund(notification: Dict[str, Any], gateway_config: GatewayConfig):
228 | # TODO: handle_failed_refund
229 | pass
230 |
231 |
232 | def handle_reversed_refund(
233 | notification: Dict[str, Any], _gateway_config: GatewayConfig
234 | ):
235 | # TODO: handle_reversed_refund
236 | pass
237 |
238 |
239 | def handle_refund_with_data(
240 | notification: Dict[str, Any], gateway_config: GatewayConfig
241 | ):
242 | handle_refund(notification, gateway_config)
243 |
244 |
245 | def webhook_not_implemented(
246 | notification: Dict[str, Any], gateway_config: GatewayConfig
247 | ):
248 | # TODO: handle_refund
249 | pass
250 |
251 |
252 | EVENT_MAP = {
253 | "AUTHORISATION": handle_authorization,
254 | "AUTHORISATION_ADJUSTMENT": webhook_not_implemented,
255 | "CANCELLATION": handle_cancellation,
256 | "CANCEL_OR_REFUND": handle_cancel_or_refund,
257 | "CAPTURE": handle_capture,
258 | "CAPTURE_FAILED": handle_failed_capture,
259 | "HANDLED_EXTERNALLY": webhook_not_implemented,
260 | "ORDER_OPENED": webhook_not_implemented,
261 | "ORDER_CLOSED": webhook_not_implemented,
262 | "PENDING": handle_pending,
263 | "PROCESS_RETRY": webhook_not_implemented,
264 | "REFUND": handle_refund,
265 | "REFUND_FAILED": handle_failed_refund,
266 | "REFUNDED_REVERSED": handle_reversed_refund,
267 | "REFUND_WITH_DATA": handle_refund_with_data,
268 | "REPORT_AVAILABLE": webhook_not_implemented,
269 | "VOID_PENDING_REFUND": webhook_not_implemented,
270 | }
271 |
272 |
273 | @transaction_with_commit_on_errors()
274 | def handle_additional_actions(
275 | request: WSGIRequest, gateway_config: "GatewayConfig"
276 | ):
277 | payment_id = request.GET.get("payment")
278 | checkout_pk = request.GET.get("checkout")
279 |
280 | if not payment_id or not checkout_pk:
281 | return HttpResponseNotFound()
282 |
283 | payment = get_payment(payment_id, transaction_id=None)
284 | if not payment:
285 | return HttpResponseNotFound(
286 | "Cannot perform payment.There is no active sberbank payment."
287 | )
288 | if not payment.checkout or str(payment.checkout.token) != checkout_pk:
289 | return HttpResponseNotFound(
290 | "Cannot perform payment.There is no checkout with this payment."
291 | )
292 |
293 | extra_data = json.loads(payment.extra_data)
294 | data = extra_data[-1] if isinstance(extra_data, list) else extra_data
295 |
296 | return_url = payment.return_url
297 |
298 | if not return_url:
299 | return HttpResponseNotFound(
300 | "Cannot perform payment. Lack of data about returnUrl."
301 | )
302 |
303 | try:
304 | request_data = prepare_api_request_data(request, data, payment.pk, checkout_pk)
305 | except KeyError as e:
306 |
307 | return HttpResponseBadRequest(e.args[0])
308 | try:
309 | result = api_call(request_data, gateway_config)
310 | except PaymentError as e:
311 | return HttpResponseBadRequest(str(e))
312 |
313 | handle_api_response(payment, result)
314 |
315 | redirect_url = prepare_redirect_url(payment_id, checkout_pk, result, return_url)
316 | return redirect(redirect_url)
317 |
318 |
319 | def prepare_api_request_data(request: WSGIRequest, data: dict, payment_pk, checkout_pk):
320 | params = request.GET
321 | request_data: "QueryDict" = QueryDict("")
322 |
323 | if all([param in request.GET for param in params]):
324 | request_data = request.GET
325 | elif all([param in request.POST for param in params]):
326 | request_data = request.POST
327 |
328 | if not request_data:
329 | raise KeyError(
330 | "Cannot perform payment. Lack of required parameters in request."
331 | )
332 |
333 | api_request_data = {
334 | "data": data,
335 | "payment_id": payment_pk,
336 | "checkout_pk": checkout_pk,
337 | "details": {key: request_data[key] for key in params},
338 | }
339 | return api_request_data
340 |
341 |
342 | def prepare_redirect_url(
343 | payment_id: str, checkout_pk: str, api_response: Adyen.Adyen, return_url: str
344 | ):
345 | checkout_id = graphene.Node.to_global_id(
346 | "Checkout", checkout_pk # type: ignore
347 | )
348 |
349 | params = {
350 | "checkout": checkout_id,
351 | "payment": payment_id,
352 | "resultCode": api_response.get("errorMessage"),
353 | }
354 |
355 | # Check if further action is needed.
356 | # if "action" in api_response.message:
357 | # params.update(api_response.message["action"])
358 |
359 | return prepare_url(urlencode(params), return_url)
360 |
361 |
362 | def handle_api_response(
363 | payment: Payment, response: Adyen.Adyen,
364 | ):
365 | checkout = get_checkout(payment)
366 | payment_data = create_payment_information(
367 | payment=payment, payment_token=payment.token,
368 | )
369 |
370 | error_message = response.get('errorMessage')
371 |
372 | result_code = response.get('errorCode')
373 | is_success = result_code not in FAILED_STATUSES
374 |
375 | token = ''
376 | list_attributes = response.get('attributes')
377 | if list_attributes:
378 | if len(list_attributes) > 0:
379 | token = list_attributes[0].get('value')
380 |
381 | gateway_response = GatewayResponse(
382 | is_success=is_success,
383 | action_required=False,
384 | kind=TransactionKind.ACTION_TO_CONFIRM,
385 | amount=payment_data.amount,
386 | currency=payment_data.currency,
387 | transaction_id=token,
388 | error=error_message,
389 | raw_response=response,
390 | searchable_key=token,
391 | )
392 |
393 | create_transaction(
394 | payment=payment,
395 | kind=TransactionKind.ACTION_TO_CONFIRM,
396 | action_required=False,
397 | payment_information=payment_data,
398 | gateway_response=gateway_response,
399 | )
400 |
401 | if is_success:
402 | create_order(payment, checkout)
403 |
--------------------------------------------------------------------------------
/storefront/src/@next/pages/CheckoutPage/CheckoutPage.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef, useState } from "react";
2 | import { useIntl } from "react-intl";
3 | import { Redirect, useLocation, useHistory } from "react-router-dom";
4 |
5 | import { Button, Loader } from "@components/atoms";
6 | import { CheckoutProgressBar } from "@components/molecules";
7 | import {
8 | CartSummary,
9 | PaymentGatewaysList,
10 | translateAdyenConfirmationError,
11 | adyenNotNegativeConfirmationStatusCodes,
12 | sberbankNotNegativeConfirmationStatusCodes,
13 | } from "@components/organisms";
14 | import { Checkout } from "@components/templates";
15 | import { useCart, useCheckout } from "@saleor/sdk";
16 | import { IItems } from "@saleor/sdk/lib/api/Cart/types";
17 | import { CHECKOUT_STEPS, CheckoutStep } from "@temp/core/config";
18 | import { checkoutMessages } from "@temp/intl";
19 | import { ITaxedMoney, ICheckoutStep, ICardData, IFormError } from "@types";
20 | import { parseQueryString } from "@temp/core/utils";
21 | import { CompleteCheckout_checkoutComplete_order } from "@saleor/sdk/lib/mutations/gqlTypes/CompleteCheckout";
22 |
23 | import { CheckoutRouter } from "./CheckoutRouter";
24 | import {
25 | CheckoutAddressSubpage,
26 | CheckoutPaymentSubpage,
27 | CheckoutReviewSubpage,
28 | CheckoutShippingSubpage,
29 | ICheckoutAddressSubpageHandles,
30 | ICheckoutPaymentSubpageHandles,
31 | ICheckoutReviewSubpageHandles,
32 | ICheckoutShippingSubpageHandles,
33 | } from "./subpages";
34 | import { IProps } from "./types";
35 |
36 | const prepareCartSummary = (
37 | totalPrice?: ITaxedMoney | null,
38 | subtotalPrice?: ITaxedMoney | null,
39 | shippingTaxedPrice?: ITaxedMoney | null,
40 | promoTaxedPrice?: ITaxedMoney | null,
41 | items?: IItems
42 | ) => {
43 | const products = items?.map(({ id, variant, totalPrice, quantity }) => ({
44 | id: id || "",
45 | name: variant.name || "",
46 | price: {
47 | gross: {
48 | amount: totalPrice?.gross.amount || 0,
49 | currency: totalPrice?.gross.currency || "",
50 | },
51 | net: {
52 | amount: totalPrice?.net.amount || 0,
53 | currency: totalPrice?.net.currency || "",
54 | },
55 | },
56 | quantity,
57 | sku: variant.sku || "",
58 | thumbnail: {
59 | alt: variant.product?.thumbnail?.alt || undefined,
60 | url: variant.product?.thumbnail?.url,
61 | url2x: variant.product?.thumbnail2x?.url,
62 | },
63 | }));
64 |
65 | return (
66 |