Payments: Support more providers (#1641)
This commit is contained in:
parent
f61db93008
commit
d6a9ba5683
@ -116,6 +116,7 @@ export function buildPaymentForm(form: GramJs.payments.PaymentForm) {
|
|||||||
needCountry: nativeData.need_country,
|
needCountry: nativeData.need_country,
|
||||||
needZip: nativeData.need_zip,
|
needZip: nativeData.need_zip,
|
||||||
publishableKey: nativeData.publishable_key,
|
publishableKey: nativeData.publishable_key,
|
||||||
|
publicToken: nativeData?.public_token,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,13 @@ import { Api as GramJs } from '../../../lib/gramjs';
|
|||||||
import { invokeRequest } from './client';
|
import { invokeRequest } from './client';
|
||||||
import { buildInputPeer, buildShippingInfo } from '../gramjsBuilders';
|
import { buildInputPeer, buildShippingInfo } from '../gramjsBuilders';
|
||||||
import { buildShippingOptions, buildPaymentForm, buildReceipt } from '../apiBuilders/payments';
|
import { buildShippingOptions, buildPaymentForm, buildReceipt } from '../apiBuilders/payments';
|
||||||
import { ApiChat } from '../../types';
|
import { ApiChat, OnApiUpdate } from '../../types';
|
||||||
|
|
||||||
|
let onUpdate: OnApiUpdate;
|
||||||
|
|
||||||
|
export function init(_onUpdate: OnApiUpdate) {
|
||||||
|
onUpdate = _onUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
export async function validateRequestedInfo({
|
export async function validateRequestedInfo({
|
||||||
chat,
|
chat,
|
||||||
@ -40,7 +46,7 @@ export async function validateRequestedInfo({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sendPaymentForm({
|
export async function sendPaymentForm({
|
||||||
chat,
|
chat,
|
||||||
messageId,
|
messageId,
|
||||||
formId,
|
formId,
|
||||||
@ -55,7 +61,7 @@ export function sendPaymentForm({
|
|||||||
requestedInfoId?: string;
|
requestedInfoId?: string;
|
||||||
shippingOptionId?: string;
|
shippingOptionId?: string;
|
||||||
}) {
|
}) {
|
||||||
return invokeRequest(new GramJs.payments.SendPaymentForm({
|
const result = await invokeRequest(new GramJs.payments.SendPaymentForm({
|
||||||
formId: BigInt(formId),
|
formId: BigInt(formId),
|
||||||
peer: buildInputPeer(chat.id, chat.accessHash),
|
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||||
msgId: messageId,
|
msgId: messageId,
|
||||||
@ -65,7 +71,18 @@ export function sendPaymentForm({
|
|||||||
save: credentials.save,
|
save: credentials.save,
|
||||||
data: new GramJs.DataJSON({ data: JSON.stringify(credentials.data) }),
|
data: new GramJs.DataJSON({ data: JSON.stringify(credentials.data) }),
|
||||||
}),
|
}),
|
||||||
}), true);
|
}));
|
||||||
|
|
||||||
|
if (result instanceof GramJs.payments.PaymentVerificationNeeded) {
|
||||||
|
onUpdate({
|
||||||
|
'@type': 'updatePaymentVerificationNeeded',
|
||||||
|
url: result.url,
|
||||||
|
});
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Boolean(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPaymentForm({
|
export async function getPaymentForm({
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import { init as initStickers } from './methods/symbols';
|
|||||||
import { init as initManagement } from './methods/management';
|
import { init as initManagement } from './methods/management';
|
||||||
import { init as initTwoFaSettings } from './methods/twoFaSettings';
|
import { init as initTwoFaSettings } from './methods/twoFaSettings';
|
||||||
import { init as initCalls } from './methods/calls';
|
import { init as initCalls } from './methods/calls';
|
||||||
|
import { init as initPayments } from './methods/payments';
|
||||||
import * as methods from './methods';
|
import * as methods from './methods';
|
||||||
|
|
||||||
let onUpdate: OnApiUpdate;
|
let onUpdate: OnApiUpdate;
|
||||||
@ -34,6 +35,7 @@ export async function initApi(_onUpdate: OnApiUpdate, initialArgs: ApiInitialArg
|
|||||||
initManagement(handleUpdate);
|
initManagement(handleUpdate);
|
||||||
initTwoFaSettings(handleUpdate);
|
initTwoFaSettings(handleUpdate);
|
||||||
initCalls(handleUpdate);
|
initCalls(handleUpdate);
|
||||||
|
initPayments(handleUpdate);
|
||||||
|
|
||||||
await initClient(handleUpdate, initialArgs);
|
await initClient(handleUpdate, initialArgs);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -188,7 +188,11 @@ export function updater(update: Update, originRequest?: GramJs.AnyRequest) {
|
|||||||
if (update.message instanceof GramJs.MessageService) {
|
if (update.message instanceof GramJs.MessageService) {
|
||||||
const { action } = update.message;
|
const { action } = update.message;
|
||||||
|
|
||||||
if (action instanceof GramJs.MessageActionChatEditTitle) {
|
if (action instanceof GramJs.MessageActionPaymentSent) {
|
||||||
|
onUpdate({
|
||||||
|
'@type': 'updatePaymentStateCompleted',
|
||||||
|
});
|
||||||
|
} else if (action instanceof GramJs.MessageActionChatEditTitle) {
|
||||||
onUpdate({
|
onUpdate({
|
||||||
'@type': 'updateChat',
|
'@type': 'updateChat',
|
||||||
id: message.chatId,
|
id: message.chatId,
|
||||||
|
|||||||
@ -32,12 +32,15 @@ export interface ApiPaymentForm {
|
|||||||
currency?: string;
|
currency?: string;
|
||||||
prices?: ApiLabeledPrice[];
|
prices?: ApiLabeledPrice[];
|
||||||
};
|
};
|
||||||
nativeParams: {
|
nativeParams: ApiPaymentFormNativeParams;
|
||||||
needCardholderName: boolean;
|
}
|
||||||
needCountry: boolean;
|
|
||||||
needZip: boolean;
|
export interface ApiPaymentFormNativeParams {
|
||||||
publishableKey: string;
|
needCardholderName?: boolean;
|
||||||
};
|
needCountry?: boolean;
|
||||||
|
needZip?: boolean;
|
||||||
|
publishableKey?: string;
|
||||||
|
publicToken?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiLabeledPrice {
|
export interface ApiLabeledPrice {
|
||||||
|
|||||||
@ -381,6 +381,15 @@ export type ApiUpdatePeerBlocked = {
|
|||||||
isBlocked: boolean;
|
isBlocked: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ApiUpdatePaymentVerificationNeeded = {
|
||||||
|
'@type': 'updatePaymentVerificationNeeded';
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ApiUpdatePaymentStateCompleted = {
|
||||||
|
'@type': 'updatePaymentStateCompleted';
|
||||||
|
};
|
||||||
|
|
||||||
export type ApiUpdatePrivacy = {
|
export type ApiUpdatePrivacy = {
|
||||||
'@type': 'updatePrivacy';
|
'@type': 'updatePrivacy';
|
||||||
key: 'phoneNumber' | 'lastSeen' | 'profilePhoto' | 'forwards' | 'chatInvite';
|
key: 'phoneNumber' | 'lastSeen' | 'profilePhoto' | 'forwards' | 'chatInvite';
|
||||||
@ -467,7 +476,7 @@ export type ApiUpdate = (
|
|||||||
ApiUpdateServerTimeOffset | ApiUpdateShowInvite | ApiUpdateMessageReactions |
|
ApiUpdateServerTimeOffset | ApiUpdateShowInvite | ApiUpdateMessageReactions |
|
||||||
ApiUpdateGroupCallParticipants | ApiUpdateGroupCallConnection | ApiUpdateGroupCall | ApiUpdateGroupCallStreams |
|
ApiUpdateGroupCallParticipants | ApiUpdateGroupCallConnection | ApiUpdateGroupCall | ApiUpdateGroupCallStreams |
|
||||||
ApiUpdateGroupCallConnectionState | ApiUpdateGroupCallLeavePresentation | ApiUpdateGroupCallChatId |
|
ApiUpdateGroupCallConnectionState | ApiUpdateGroupCallLeavePresentation | ApiUpdateGroupCallChatId |
|
||||||
ApiUpdatePendingJoinRequests
|
ApiUpdatePendingJoinRequests | ApiUpdatePaymentVerificationNeeded | ApiUpdatePaymentStateCompleted
|
||||||
);
|
);
|
||||||
|
|
||||||
export type OnApiUpdate = (update: ApiUpdate) => void;
|
export type OnApiUpdate = (update: ApiUpdate) => void;
|
||||||
|
|||||||
BIN
src/assets/smartglocal-logo.png
Normal file
BIN
src/assets/smartglocal-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 402 B |
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
// Force rendering in the composite layer to fix the z-index rendering issue
|
// Force rendering in the composite layer to fix the z-index rendering issue
|
||||||
transform: translate3d(0, 0, 10px);
|
transform: translate3d(0, 0, 0.625rem);
|
||||||
transform-style: preserve-3d;
|
transform-style: preserve-3d;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 3.375rem;
|
height: 3.375rem;
|
||||||
max-width: 274px;
|
max-width: 274px;
|
||||||
transform: translate3d(-50%, 0, 10px);
|
transform: translate3d(-50%, 0, 0.625rem);
|
||||||
transition: opacity 0.3s ease-in;
|
transition: opacity 0.3s ease-in;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
||||||
|
|||||||
@ -2,9 +2,10 @@ import React, {
|
|||||||
FC, memo, useCallback, useState, useRef, useEffect,
|
FC, memo, useCallback, useState, useRef, useEffect,
|
||||||
} from '../../lib/teact/teact';
|
} from '../../lib/teact/teact';
|
||||||
|
|
||||||
import useFocusAfterAnimation from '../../hooks/useFocusAfterAnimation';
|
|
||||||
import { formatCardNumber } from '../middle/helpers/inputFormatters';
|
import { formatCardNumber } from '../middle/helpers/inputFormatters';
|
||||||
import { detectCardType, CardType } from '../common/helpers/detectCardType';
|
import { detectCardType, CardType } from '../common/helpers/detectCardType';
|
||||||
|
import useFocusAfterAnimation from '../../hooks/useFocusAfterAnimation';
|
||||||
|
import useLang from '../../hooks/useLang';
|
||||||
|
|
||||||
import InputText from '../ui/InputText';
|
import InputText from '../ui/InputText';
|
||||||
|
|
||||||
@ -22,6 +23,7 @@ export type OwnProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const CardInput : FC<OwnProps> = ({ value, error, onChange }) => {
|
const CardInput : FC<OwnProps> = ({ value, error, onChange }) => {
|
||||||
|
const lang = useLang();
|
||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const cardNumberRef = useRef<HTMLInputElement>(null);
|
const cardNumberRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
@ -51,7 +53,7 @@ const CardInput : FC<OwnProps> = ({ value, error, onChange }) => {
|
|||||||
<span className="left-addon">{cardIcon}</span>
|
<span className="left-addon">{cardIcon}</span>
|
||||||
<InputText
|
<InputText
|
||||||
ref={cardNumberRef}
|
ref={cardNumberRef}
|
||||||
label="Card number"
|
label={lang('PaymentCardNumber')}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
value={value}
|
value={value}
|
||||||
inputMode="numeric"
|
inputMode="numeric"
|
||||||
|
|||||||
@ -63,13 +63,21 @@
|
|||||||
width: 1.5rem;
|
width: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
i.stripe-provider {
|
i.provider {
|
||||||
background: url("../../assets/stripe-logo.png") no-repeat center;
|
background: no-repeat center;
|
||||||
background-size: 2rem;
|
background-size: 2rem;
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
height: 1.5rem;
|
height: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i.provider.stripe {
|
||||||
|
background-image: url("../../assets/stripe-logo.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
i.provider.smartglocal {
|
||||||
|
background-image: url("../../assets/smartglocal-logo.png");
|
||||||
|
}
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
.title {
|
.title {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import React, {
|
|||||||
import { LangCode, Price } from '../../types';
|
import { LangCode, Price } from '../../types';
|
||||||
|
|
||||||
import { formatCurrency } from '../../util/formatCurrency';
|
import { formatCurrency } from '../../util/formatCurrency';
|
||||||
|
import buildClassName from '../../util/buildClassName';
|
||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
|
|
||||||
import './Checkout.scss';
|
import './Checkout.scss';
|
||||||
@ -70,12 +71,16 @@ const Checkout: FC<OwnProps> = ({
|
|||||||
) }
|
) }
|
||||||
</div>
|
</div>
|
||||||
<div className="invoice-info">
|
<div className="invoice-info">
|
||||||
{paymentMethod && renderCheckoutItem('icon-card', paymentMethod, 'Payment method')}
|
{paymentMethod && renderCheckoutItem('icon-card', paymentMethod, lang('PaymentCheckoutMethod'))}
|
||||||
{paymentProvider && renderCheckoutItem('stripe-provider', paymentProvider, 'Payment provider')}
|
{paymentProvider && renderCheckoutItem(
|
||||||
{shippingAddress && renderCheckoutItem('icon-location', shippingAddress, 'Shipping address')}
|
buildClassName('provider', paymentProvider.toLowerCase()),
|
||||||
{name && renderCheckoutItem('icon-user', name, 'Name')}
|
paymentProvider,
|
||||||
{phone && renderCheckoutItem('icon-phone', phone, 'Phone number')}
|
lang('PaymentCheckoutProvider'),
|
||||||
{shippingMethod && renderCheckoutItem('icon-truck', shippingMethod, 'Shipping method')}
|
)}
|
||||||
|
{shippingAddress && renderCheckoutItem('icon-location', shippingAddress, lang('PaymentShippingAddress'))}
|
||||||
|
{name && renderCheckoutItem('icon-user', name, lang('PaymentCheckoutName'))}
|
||||||
|
{phone && renderCheckoutItem('icon-phone', phone, lang('PaymentCheckoutPhoneNumber'))}
|
||||||
|
{shippingMethod && renderCheckoutItem('icon-truck', shippingMethod, lang('PaymentCheckoutShippingMethod'))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
13
src/components/payment/ConfirmPayment.scss
Normal file
13
src/components/payment/ConfirmPayment.scss
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
.ConfirmPayment {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
border-bottom-left-radius: var(--border-radius-default-small);
|
||||||
|
border-bottom-right-radius: var(--border-radius-default-small);
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
27
src/components/payment/ConfirmPayment.tsx
Normal file
27
src/components/payment/ConfirmPayment.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import React, { FC, memo } from '../../lib/teact/teact';
|
||||||
|
|
||||||
|
import useLang from '../../hooks/useLang';
|
||||||
|
|
||||||
|
import './ConfirmPayment.scss';
|
||||||
|
|
||||||
|
export type OwnProps = {
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ConfirmPayment: FC<OwnProps> = ({ url }) => {
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="ConfirmPayment">
|
||||||
|
<iframe
|
||||||
|
src={url}
|
||||||
|
title={lang('Checkout.WebConfirmation.Title')}
|
||||||
|
allow="payment"
|
||||||
|
sandbox="allow-forms allow-scripts allow-same-origin allow-top-navigation"
|
||||||
|
className="ConfirmPayment__content"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(ConfirmPayment);
|
||||||
@ -5,6 +5,7 @@ import React, {
|
|||||||
import { formatCardExpiry } from '../middle/helpers/inputFormatters';
|
import { formatCardExpiry } from '../middle/helpers/inputFormatters';
|
||||||
|
|
||||||
import InputText from '../ui/InputText';
|
import InputText from '../ui/InputText';
|
||||||
|
import useLang from '../../hooks/useLang';
|
||||||
|
|
||||||
const MAX_FIELD_LENGTH = 5;
|
const MAX_FIELD_LENGTH = 5;
|
||||||
|
|
||||||
@ -15,6 +16,7 @@ export type OwnProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ExpiryInput : FC<OwnProps> = ({ value, error, onChange }) => {
|
const ExpiryInput : FC<OwnProps> = ({ value, error, onChange }) => {
|
||||||
|
const lang = useLang();
|
||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const expiryInputRef = useRef<HTMLInputElement>(null);
|
const expiryInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
@ -33,7 +35,7 @@ const ExpiryInput : FC<OwnProps> = ({ value, error, onChange }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<InputText
|
<InputText
|
||||||
label="Expiry date"
|
label={lang('PaymentCardExpireDate')}
|
||||||
ref={expiryInputRef}
|
ref={expiryInputRef}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
|
|||||||
@ -84,7 +84,7 @@ const PaymentInfo: FC<OwnProps> = ({
|
|||||||
/>
|
/>
|
||||||
{ needCardholderName && (
|
{ needCardholderName && (
|
||||||
<InputText
|
<InputText
|
||||||
label="Name on card"
|
label={lang('PaymentCardName')}
|
||||||
onChange={handleCardholderChange}
|
onChange={handleCardholderChange}
|
||||||
value={state.cardholder}
|
value={state.cardholder}
|
||||||
inputMode="text"
|
inputMode="text"
|
||||||
@ -98,7 +98,7 @@ const PaymentInfo: FC<OwnProps> = ({
|
|||||||
error={formErrors.expiry}
|
error={formErrors.expiry}
|
||||||
/>
|
/>
|
||||||
<InputText
|
<InputText
|
||||||
label="CVV code"
|
label={lang('lng_payments_card_cvc')}
|
||||||
onChange={handleCvvChange}
|
onChange={handleCvvChange}
|
||||||
value={state.cvv}
|
value={state.cvv}
|
||||||
inputMode="numeric"
|
inputMode="numeric"
|
||||||
@ -111,8 +111,8 @@ const PaymentInfo: FC<OwnProps> = ({
|
|||||||
) : undefined }
|
) : undefined }
|
||||||
{ needCountry && (
|
{ needCountry && (
|
||||||
<Select
|
<Select
|
||||||
label="Country"
|
label={lang('PaymentShippingCountry')}
|
||||||
placeholder="Country"
|
placeholder={lang('PaymentShippingCountry')}
|
||||||
onChange={handleCountryChange}
|
onChange={handleCountryChange}
|
||||||
value={state.billingCountry}
|
value={state.billingCountry}
|
||||||
hasArrow={Boolean(true)}
|
hasArrow={Boolean(true)}
|
||||||
@ -134,7 +134,7 @@ const PaymentInfo: FC<OwnProps> = ({
|
|||||||
) }
|
) }
|
||||||
{ needZip && (
|
{ needZip && (
|
||||||
<InputText
|
<InputText
|
||||||
label="Post Code"
|
label={lang('PaymentShippingZipPlaceholder')}
|
||||||
onChange={handleBillingPostCodeChange}
|
onChange={handleBillingPostCodeChange}
|
||||||
value={state.billingZip}
|
value={state.billingZip}
|
||||||
inputMode="text"
|
inputMode="text"
|
||||||
|
|||||||
@ -5,8 +5,8 @@
|
|||||||
|
|
||||||
.header {
|
.header {
|
||||||
position: relative;
|
position: relative;
|
||||||
border-top-left-radius: 10px;
|
border-top-left-radius: var(--border-radius-default-small);
|
||||||
border-top-right-radius: 10px;
|
border-top-right-radius: var(--border-radius-default-small);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0.25rem 1rem;
|
padding: 0.25rem 1rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -49,8 +49,8 @@
|
|||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
position: relative;
|
position: relative;
|
||||||
border-bottom-left-radius: 10px;
|
border-bottom-left-radius: var(--border-radius-default-small);
|
||||||
border-bottom-right-radius: 10px;
|
border-bottom-right-radius: var(--border-radius-default-small);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0.75rem 1rem;
|
padding: 0.75rem 1rem;
|
||||||
background: var(--color-background);
|
background: var(--color-background);
|
||||||
|
|||||||
@ -19,10 +19,13 @@ import Button from '../ui/Button';
|
|||||||
import Modal from '../ui/Modal';
|
import Modal from '../ui/Modal';
|
||||||
import Transition from '../ui/Transition';
|
import Transition from '../ui/Transition';
|
||||||
import Spinner from '../ui/Spinner';
|
import Spinner from '../ui/Spinner';
|
||||||
|
import ConfirmPayment from './ConfirmPayment';
|
||||||
|
|
||||||
import './PaymentModal.scss';
|
import './PaymentModal.scss';
|
||||||
|
|
||||||
const DEFAULT_PROVIDER = 'stripe';
|
const DEFAULT_PROVIDER = 'stripe';
|
||||||
|
const DONATE_PROVIDER = 'smartglocal';
|
||||||
|
const SUPPORTED_PROVIDERS = new Set([DEFAULT_PROVIDER, DONATE_PROVIDER]);
|
||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@ -43,6 +46,7 @@ type StateProps = {
|
|||||||
needCardholderName?: boolean;
|
needCardholderName?: boolean;
|
||||||
needCountry?: boolean;
|
needCountry?: boolean;
|
||||||
needZip?: boolean;
|
needZip?: boolean;
|
||||||
|
confirmPaymentUrl?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type GlobalStateProps = Pick<GlobalState['payment'], (
|
type GlobalStateProps = Pick<GlobalState['payment'], (
|
||||||
@ -73,6 +77,7 @@ const Invoice: FC<OwnProps & StateProps & GlobalStateProps> = ({
|
|||||||
needCardholderName,
|
needCardholderName,
|
||||||
needCountry,
|
needCountry,
|
||||||
needZip,
|
needZip,
|
||||||
|
confirmPaymentUrl,
|
||||||
error,
|
error,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
@ -86,6 +91,7 @@ const Invoice: FC<OwnProps & StateProps & GlobalStateProps> = ({
|
|||||||
const [paymentState, paymentDispatch] = usePaymentReducer();
|
const [paymentState, paymentDispatch] = usePaymentReducer();
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
const canRenderFooter = step !== PaymentStep.ConfirmPayment;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (step || error) {
|
if (step || error) {
|
||||||
@ -210,6 +216,12 @@ const Invoice: FC<OwnProps & StateProps & GlobalStateProps> = ({
|
|||||||
currency={currency}
|
currency={currency}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
case PaymentStep.ConfirmPayment:
|
||||||
|
return (
|
||||||
|
<ConfirmPayment
|
||||||
|
url={confirmPaymentUrl!}
|
||||||
|
/>
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -266,6 +278,8 @@ const Invoice: FC<OwnProps & StateProps & GlobalStateProps> = ({
|
|||||||
return lang('PaymentCardInfo');
|
return lang('PaymentCardInfo');
|
||||||
case PaymentStep.Checkout:
|
case PaymentStep.Checkout:
|
||||||
return lang('PaymentCheckout');
|
return lang('PaymentCheckout');
|
||||||
|
case PaymentStep.ConfirmPayment:
|
||||||
|
return lang('Checkout.WebConfirmation.Title');
|
||||||
default:
|
default:
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@ -331,16 +345,18 @@ const Invoice: FC<OwnProps & StateProps & GlobalStateProps> = ({
|
|||||||
<Spinner color="gray" />
|
<Spinner color="gray" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="footer">
|
{canRenderFooter && (
|
||||||
<Button
|
<div className="footer">
|
||||||
type="submit"
|
<Button
|
||||||
onClick={handleButtonClick}
|
type="submit"
|
||||||
disabled={isLoading}
|
onClick={handleButtonClick}
|
||||||
isLoading={isLoading}
|
disabled={isLoading}
|
||||||
>
|
isLoading={isLoading}
|
||||||
{buttonText}
|
>
|
||||||
</Button>
|
{buttonText}
|
||||||
</div>
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{error && !error.field && renderError()}
|
{error && !error.field && renderError()}
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
@ -359,9 +375,10 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
nativeParams,
|
nativeParams,
|
||||||
passwordMissing,
|
passwordMissing,
|
||||||
error,
|
error,
|
||||||
|
confirmPaymentUrl,
|
||||||
} = global.payment;
|
} = global.payment;
|
||||||
|
|
||||||
const isProviderError = Boolean(invoice && (!nativeProvider || nativeProvider !== DEFAULT_PROVIDER));
|
const isProviderError = Boolean(invoice && (!nativeProvider || !SUPPORTED_PROVIDERS.has(nativeProvider)));
|
||||||
const { needCardholderName, needCountry, needZip } = (nativeParams || {});
|
const { needCardholderName, needCountry, needZip } = (nativeParams || {});
|
||||||
const {
|
const {
|
||||||
nameRequested,
|
nameRequested,
|
||||||
@ -397,6 +414,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
needCountry,
|
needCountry,
|
||||||
needZip,
|
needZip,
|
||||||
error,
|
error,
|
||||||
|
confirmPaymentUrl,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
)(Invoice));
|
)(Invoice));
|
||||||
|
|||||||
@ -47,7 +47,7 @@ const Shipping: FC<OwnProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div className="Shipping">
|
<div className="Shipping">
|
||||||
<form>
|
<form>
|
||||||
<p>Select shipping method</p>
|
<p>{lang('PaymentShippingMethod')}</p>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
name="shipping-options"
|
name="shipping-options"
|
||||||
options={options}
|
options={options}
|
||||||
|
|||||||
@ -102,36 +102,36 @@ const ShippingInfo: FC<OwnProps> = ({
|
|||||||
<h5>{lang('PaymentShippingAddress')}</h5>
|
<h5>{lang('PaymentShippingAddress')}</h5>
|
||||||
<InputText
|
<InputText
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
label="Address1 (Street)"
|
label={lang('PaymentShippingAddress1Placeholder')}
|
||||||
onChange={handleAddress1Change}
|
onChange={handleAddress1Change}
|
||||||
value={state.streetLine1}
|
value={state.streetLine1}
|
||||||
inputMode="text"
|
inputMode="text"
|
||||||
error={formErrors.streetLine1}
|
error={formErrors.streetLine1}
|
||||||
/>
|
/>
|
||||||
<InputText
|
<InputText
|
||||||
label="Address2 (Street)"
|
label={lang('PaymentShippingAddress2Placeholder')}
|
||||||
onChange={handleAddress2Change}
|
onChange={handleAddress2Change}
|
||||||
value={state.streetLine2}
|
value={state.streetLine2}
|
||||||
inputMode="text"
|
inputMode="text"
|
||||||
error={formErrors.streetLine2}
|
error={formErrors.streetLine2}
|
||||||
/>
|
/>
|
||||||
<InputText
|
<InputText
|
||||||
label="City"
|
label={lang('PaymentShippingCityPlaceholder')}
|
||||||
onChange={handleCityChange}
|
onChange={handleCityChange}
|
||||||
value={state.city}
|
value={state.city}
|
||||||
inputMode="text"
|
inputMode="text"
|
||||||
error={formErrors.city}
|
error={formErrors.city}
|
||||||
/>
|
/>
|
||||||
<InputText
|
<InputText
|
||||||
label="State"
|
label={lang('PaymentShippingStatePlaceholder')}
|
||||||
onChange={handleStateChange}
|
onChange={handleStateChange}
|
||||||
value={state.state}
|
value={state.state}
|
||||||
inputMode="text"
|
inputMode="text"
|
||||||
error={formErrors.state}
|
error={formErrors.state}
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
label="Country"
|
label={lang('PaymentShippingCountry')}
|
||||||
placeholder="Country"
|
placeholder={lang('PaymentShippingCountry')}
|
||||||
onChange={handleCountryChange}
|
onChange={handleCountryChange}
|
||||||
value={state.countryIso2}
|
value={state.countryIso2}
|
||||||
hasArrow={Boolean(true)}
|
hasArrow={Boolean(true)}
|
||||||
@ -150,7 +150,7 @@ const ShippingInfo: FC<OwnProps> = ({
|
|||||||
</Select>
|
</Select>
|
||||||
|
|
||||||
<InputText
|
<InputText
|
||||||
label="Post Code"
|
label={lang('PaymentShippingZipPlaceholder')}
|
||||||
onChange={handlePostCodeChange}
|
onChange={handlePostCodeChange}
|
||||||
value={state.postCode}
|
value={state.postCode}
|
||||||
inputMode="text"
|
inputMode="text"
|
||||||
@ -163,7 +163,7 @@ const ShippingInfo: FC<OwnProps> = ({
|
|||||||
) : undefined }
|
) : undefined }
|
||||||
{ needName && (
|
{ needName && (
|
||||||
<InputText
|
<InputText
|
||||||
label="Full name"
|
label={lang('PaymentShippingName')}
|
||||||
onChange={handleFullNameChange}
|
onChange={handleFullNameChange}
|
||||||
value={state.fullName}
|
value={state.fullName}
|
||||||
inputMode="text"
|
inputMode="text"
|
||||||
@ -172,7 +172,7 @@ const ShippingInfo: FC<OwnProps> = ({
|
|||||||
) }
|
) }
|
||||||
{ needEmail && (
|
{ needEmail && (
|
||||||
<InputText
|
<InputText
|
||||||
label="Email"
|
label={lang('PaymentShippingEmailPlaceholder')}
|
||||||
onChange={handleEmailChange}
|
onChange={handleEmailChange}
|
||||||
value={state.email}
|
value={state.email}
|
||||||
inputMode="email"
|
inputMode="email"
|
||||||
@ -181,7 +181,7 @@ const ShippingInfo: FC<OwnProps> = ({
|
|||||||
) }
|
) }
|
||||||
{ needPhone && (
|
{ needPhone && (
|
||||||
<InputText
|
<InputText
|
||||||
label="Phone number"
|
label={lang('PaymentShippingPhoneNumber')}
|
||||||
onChange={handlePhoneChange}
|
onChange={handlePhoneChange}
|
||||||
value={state.phone}
|
value={state.phone}
|
||||||
inputMode="tel"
|
inputMode="tel"
|
||||||
|
|||||||
@ -16,6 +16,8 @@ export const DEBUG_GRAMJS = false;
|
|||||||
export const PAGE_TITLE = 'Telegram';
|
export const PAGE_TITLE = 'Telegram';
|
||||||
export const INACTIVE_MARKER = ' [Inactive]';
|
export const INACTIVE_MARKER = ' [Inactive]';
|
||||||
|
|
||||||
|
export const DEBUG_PAYMENT_SMART_GLOCAL = false;
|
||||||
|
|
||||||
export const SESSION_USER_KEY = 'user_auth';
|
export const SESSION_USER_KEY = 'user_auth';
|
||||||
export const LEGACY_SESSION_KEY = 'GramJs:sessionId';
|
export const LEGACY_SESSION_KEY = 'GramJs:sessionId';
|
||||||
|
|
||||||
|
|||||||
@ -26,6 +26,7 @@ import {
|
|||||||
ApiAvailableReaction,
|
ApiAvailableReaction,
|
||||||
ApiAppConfig,
|
ApiAppConfig,
|
||||||
ApiSponsoredMessage,
|
ApiSponsoredMessage,
|
||||||
|
ApiPaymentFormNativeParams,
|
||||||
} from '../api/types';
|
} from '../api/types';
|
||||||
import {
|
import {
|
||||||
FocusDirection,
|
FocusDirection,
|
||||||
@ -426,16 +427,15 @@ export type GlobalState = {
|
|||||||
};
|
};
|
||||||
nativeProvider?: string;
|
nativeProvider?: string;
|
||||||
providerId?: string;
|
providerId?: string;
|
||||||
nativeParams?: {
|
nativeParams?: ApiPaymentFormNativeParams;
|
||||||
needCardholderName: boolean;
|
|
||||||
needCountry: boolean;
|
|
||||||
needZip: boolean;
|
|
||||||
publishableKey: string;
|
|
||||||
};
|
|
||||||
stripeCredentials?: {
|
stripeCredentials?: {
|
||||||
type: string;
|
type: string;
|
||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
|
smartGlocalCredentials?: {
|
||||||
|
type: string;
|
||||||
|
token: string;
|
||||||
|
};
|
||||||
passwordMissing?: boolean;
|
passwordMissing?: boolean;
|
||||||
savedCredentials?: {
|
savedCredentials?: {
|
||||||
id: string;
|
id: string;
|
||||||
@ -448,6 +448,7 @@ export type GlobalState = {
|
|||||||
description: string;
|
description: string;
|
||||||
};
|
};
|
||||||
isPaymentModalOpen?: boolean;
|
isPaymentModalOpen?: boolean;
|
||||||
|
confirmPaymentUrl?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
chatCreation?: {
|
chatCreation?: {
|
||||||
|
|||||||
@ -34,3 +34,4 @@ import './apiUpdaters/misc';
|
|||||||
import './apiUpdaters/settings';
|
import './apiUpdaters/settings';
|
||||||
import './apiUpdaters/twoFaSettings';
|
import './apiUpdaters/twoFaSettings';
|
||||||
import './apiUpdaters/calls';
|
import './apiUpdaters/calls';
|
||||||
|
import './apiUpdaters/payments';
|
||||||
|
|||||||
@ -12,10 +12,13 @@ import {
|
|||||||
selectPaymentChatId,
|
selectPaymentChatId,
|
||||||
selectChat,
|
selectChat,
|
||||||
selectPaymentFormId,
|
selectPaymentFormId,
|
||||||
|
selectProviderPublicToken,
|
||||||
|
selectSmartGlocalCredentials,
|
||||||
} from '../../selectors';
|
} from '../../selectors';
|
||||||
import { callApi } from '../../../api/gramjs';
|
import { callApi } from '../../../api/gramjs';
|
||||||
import { getStripeError } from '../../helpers';
|
import { getStripeError } from '../../helpers';
|
||||||
import { buildQueryString } from '../../../util/requestQuery';
|
import { buildQueryString } from '../../../util/requestQuery';
|
||||||
|
import { DEBUG_PAYMENT_SMART_GLOCAL } from '../../../config';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
updateShippingOptions,
|
updateShippingOptions,
|
||||||
@ -27,6 +30,7 @@ import {
|
|||||||
setReceipt,
|
setReceipt,
|
||||||
clearPayment,
|
clearPayment,
|
||||||
closeInvoice,
|
closeInvoice,
|
||||||
|
setSmartGlocalCardInfo,
|
||||||
} from '../../reducers';
|
} from '../../reducers';
|
||||||
|
|
||||||
addReducer('validateRequestedInfo', (global, actions, payload) => {
|
addReducer('validateRequestedInfo', (global, actions, payload) => {
|
||||||
@ -132,13 +136,23 @@ addReducer('clearReceipt', (global) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
addReducer('sendCredentialsInfo', (global, actions, payload) => {
|
addReducer('sendCredentialsInfo', (global, actions, payload) => {
|
||||||
const publishableKey = selectProviderPublishableKey(global);
|
const { nativeProvider } = global.payment;
|
||||||
if (!publishableKey) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const { credentials } = payload;
|
const { credentials } = payload;
|
||||||
const { data } = credentials;
|
const { data } = credentials;
|
||||||
void sendStripeCredentials(data, publishableKey);
|
|
||||||
|
if (nativeProvider === 'stripe') {
|
||||||
|
const publishableKey = selectProviderPublishableKey(global);
|
||||||
|
if (!publishableKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
void sendStripeCredentials(data, publishableKey);
|
||||||
|
} else if (nativeProvider === 'smartglocal') {
|
||||||
|
const publicToken = selectProviderPublicToken(global);
|
||||||
|
if (!publicToken) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
void sendSmartGlocalCredentials(data, publicToken);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
addReducer('sendPaymentForm', (global, actions, payload) => {
|
addReducer('sendPaymentForm', (global, actions, payload) => {
|
||||||
@ -148,15 +162,16 @@ addReducer('sendPaymentForm', (global, actions, payload) => {
|
|||||||
const messageId = selectPaymentMessageId(global);
|
const messageId = selectPaymentMessageId(global);
|
||||||
const formId = selectPaymentFormId(global);
|
const formId = selectPaymentFormId(global);
|
||||||
const requestInfoId = selectPaymentRequestId(global);
|
const requestInfoId = selectPaymentRequestId(global);
|
||||||
const publishableKey = selectProviderPublishableKey(global);
|
const { nativeProvider } = global.payment;
|
||||||
const stripeCredentials = selectStripeCredentials(global);
|
const publishableKey = nativeProvider === 'stripe'
|
||||||
if (!chat || !messageId || !publishableKey || !formId) {
|
? selectProviderPublishableKey(global) : selectProviderPublicToken(global);
|
||||||
|
if (!chat || !messageId || !publishableKey || !formId || !nativeProvider) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendPaymentForm(chat, messageId, formId, {
|
void sendPaymentForm(chat, messageId, nativeProvider, formId, {
|
||||||
save: saveCredentials,
|
save: saveCredentials,
|
||||||
data: stripeCredentials,
|
data: nativeProvider === 'stripe' ? selectStripeCredentials(global) : selectSmartGlocalCredentials(global),
|
||||||
}, requestInfoId, shippingOptionId);
|
}, requestInfoId, shippingOptionId);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -212,9 +227,67 @@ async function sendStripeCredentials(
|
|||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function sendSmartGlocalCredentials(
|
||||||
|
data: {
|
||||||
|
cardNumber: string;
|
||||||
|
cardholder?: string;
|
||||||
|
expiryMonth: string;
|
||||||
|
expiryYear: string;
|
||||||
|
cvv: string;
|
||||||
|
},
|
||||||
|
publicToken: string,
|
||||||
|
) {
|
||||||
|
const params = {
|
||||||
|
card: {
|
||||||
|
number: data.cardNumber.replace(/[^\d]+/g, ''),
|
||||||
|
expiration_month: data.expiryMonth,
|
||||||
|
expiration_year: data.expiryYear,
|
||||||
|
security_code: data.cvv.replace(/[^\d]+/g, ''),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const url = DEBUG_PAYMENT_SMART_GLOCAL
|
||||||
|
? 'https://tgb-playground.smart-glocal.com/cds/v1/tokenize/card'
|
||||||
|
: 'https://tgb.smart-glocal.com/cds/v1/tokenize/card';
|
||||||
|
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-PUBLIC-TOKEN': publicToken,
|
||||||
|
},
|
||||||
|
body: JSON.stringify(params),
|
||||||
|
});
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.status !== 'ok') {
|
||||||
|
// TODO после получения документации сделать аналог getStripeError(result.error);
|
||||||
|
const error = { description: 'payment error' };
|
||||||
|
const global = getGlobal();
|
||||||
|
setGlobal({
|
||||||
|
...global,
|
||||||
|
payment: {
|
||||||
|
...global.payment,
|
||||||
|
error: {
|
||||||
|
...error,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let global = setSmartGlocalCardInfo(getGlobal(), {
|
||||||
|
type: 'card',
|
||||||
|
token: result.data.token,
|
||||||
|
});
|
||||||
|
global = setPaymentStep(global, PaymentStep.Checkout);
|
||||||
|
setGlobal(global);
|
||||||
|
}
|
||||||
|
|
||||||
async function sendPaymentForm(
|
async function sendPaymentForm(
|
||||||
chat: ApiChat,
|
chat: ApiChat,
|
||||||
messageId: number,
|
messageId: number,
|
||||||
|
nativeProvider: string,
|
||||||
formId: string,
|
formId: string,
|
||||||
credentials: any,
|
credentials: any,
|
||||||
requestedInfoId?: string,
|
requestedInfoId?: string,
|
||||||
@ -223,7 +296,8 @@ async function sendPaymentForm(
|
|||||||
const result = await callApi('sendPaymentForm', {
|
const result = await callApi('sendPaymentForm', {
|
||||||
chat, messageId, formId, credentials, requestedInfoId, shippingOptionId,
|
chat, messageId, formId, credentials, requestedInfoId, shippingOptionId,
|
||||||
});
|
});
|
||||||
if (result) {
|
|
||||||
|
if (result === true) {
|
||||||
const global = clearPayment(getGlobal());
|
const global = clearPayment(getGlobal());
|
||||||
setGlobal(closeInvoice(global));
|
setGlobal(closeInvoice(global));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import {
|
import { addReducer, getGlobal, setGlobal } from '../../../lib/teact/teactn';
|
||||||
addReducer, getGlobal, setGlobal,
|
|
||||||
} from '../../../lib/teact/teactn';
|
|
||||||
|
|
||||||
import { ApiUpdate } from '../../../api/types';
|
import { ApiUpdate } from '../../../api/types';
|
||||||
import { ApiPrivacyKey } from '../../../types';
|
import { ApiPrivacyKey, PaymentStep } from '../../../types';
|
||||||
|
|
||||||
import { addBlockedContact, removeBlockedContact } from '../../reducers';
|
import {
|
||||||
|
addBlockedContact, removeBlockedContact, setConfirmPaymentUrl, setPaymentStep,
|
||||||
|
} from '../../reducers';
|
||||||
|
|
||||||
addReducer('apiUpdate', (global, actions, update: ApiUpdate) => {
|
addReducer('apiUpdate', (global, actions, update: ApiUpdate) => {
|
||||||
switch (update['@type']) {
|
switch (update['@type']) {
|
||||||
@ -32,6 +32,12 @@ addReducer('apiUpdate', (global, actions, update: ApiUpdate) => {
|
|||||||
case 'updatePrivacy':
|
case 'updatePrivacy':
|
||||||
global.settings.privacy[update.key as ApiPrivacyKey] = update.rules;
|
global.settings.privacy[update.key as ApiPrivacyKey] = update.rules;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'updatePaymentVerificationNeeded':
|
||||||
|
global = setConfirmPaymentUrl(getGlobal(), update.url);
|
||||||
|
global = setPaymentStep(global, PaymentStep.ConfirmPayment);
|
||||||
|
setGlobal(global);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
15
src/modules/actions/apiUpdaters/payments.ts
Normal file
15
src/modules/actions/apiUpdaters/payments.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { addReducer } from '../../../lib/teact/teactn';
|
||||||
|
|
||||||
|
import { ApiUpdate } from '../../../api/types';
|
||||||
|
|
||||||
|
import { clearPayment } from '../../reducers';
|
||||||
|
|
||||||
|
addReducer('apiUpdate', (global, actions, update: ApiUpdate) => {
|
||||||
|
switch (update['@type']) {
|
||||||
|
case 'updatePaymentStateCompleted': {
|
||||||
|
return clearPayment(global);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
});
|
||||||
@ -75,6 +75,21 @@ export function setStripeCardInfo(global: GlobalState, cardInfo: { type: string;
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setSmartGlocalCardInfo(
|
||||||
|
global: GlobalState,
|
||||||
|
cardInfo: { type: string; token: string },
|
||||||
|
): GlobalState {
|
||||||
|
return {
|
||||||
|
...global,
|
||||||
|
payment: {
|
||||||
|
...global.payment,
|
||||||
|
smartGlocalCredentials: {
|
||||||
|
...cardInfo,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function setPaymentForm(global: GlobalState, form: ApiPaymentForm): GlobalState {
|
export function setPaymentForm(global: GlobalState, form: ApiPaymentForm): GlobalState {
|
||||||
return {
|
return {
|
||||||
...global,
|
...global,
|
||||||
@ -85,6 +100,16 @@ export function setPaymentForm(global: GlobalState, form: ApiPaymentForm): Globa
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setConfirmPaymentUrl(global: GlobalState, url?: string): GlobalState {
|
||||||
|
return {
|
||||||
|
...global,
|
||||||
|
payment: {
|
||||||
|
...global.payment,
|
||||||
|
confirmPaymentUrl: url,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function setReceipt(
|
export function setReceipt(
|
||||||
global: GlobalState,
|
global: GlobalState,
|
||||||
receipt?: ApiReceipt,
|
receipt?: ApiReceipt,
|
||||||
|
|||||||
@ -17,9 +17,17 @@ export function selectPaymentRequestId(global: GlobalState) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function selectProviderPublishableKey(global: GlobalState) {
|
export function selectProviderPublishableKey(global: GlobalState) {
|
||||||
return global.payment.nativeParams ? global.payment.nativeParams.publishableKey : undefined;
|
return global.payment.nativeParams?.publishableKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function selectProviderPublicToken(global: GlobalState) {
|
||||||
|
return global.payment.nativeParams?.publicToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function selectStripeCredentials(global: GlobalState) {
|
export function selectStripeCredentials(global: GlobalState) {
|
||||||
return global.payment.stripeCredentials;
|
return global.payment.stripeCredentials;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function selectSmartGlocalCredentials(global: GlobalState) {
|
||||||
|
return global.payment.smartGlocalCredentials;
|
||||||
|
}
|
||||||
|
|||||||
@ -318,6 +318,7 @@ export enum PaymentStep {
|
|||||||
Shipping,
|
Shipping,
|
||||||
PaymentInfo,
|
PaymentInfo,
|
||||||
Checkout,
|
Checkout,
|
||||||
|
ConfirmPayment,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const UPLOADING_WALLPAPER_SLUG = 'UPLOADING_WALLPAPER_SLUG';
|
export const UPLOADING_WALLPAPER_SLUG = 'UPLOADING_WALLPAPER_SLUG';
|
||||||
|
|||||||
@ -69,6 +69,7 @@ const READABLE_ERROR_MESSAGES: Record<string, string> = {
|
|||||||
ADMIN_RANK_INVALID: 'The specified admin rank is invalid',
|
ADMIN_RANK_INVALID: 'The specified admin rank is invalid',
|
||||||
FRESH_CHANGE_ADMINS_FORBIDDEN: 'You were just elected admin, you can\'t add or modify other admins yet',
|
FRESH_CHANGE_ADMINS_FORBIDDEN: 'You were just elected admin, you can\'t add or modify other admins yet',
|
||||||
INPUT_USER_DEACTIVATED: 'The specified user was deleted',
|
INPUT_USER_DEACTIVATED: 'The specified user was deleted',
|
||||||
|
BOT_PRECHECKOUT_TIMEOUT: 'The request for payment has expired',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SHIPPING_ERRORS: Record<string, ApiFieldError> = {
|
export const SHIPPING_ERRORS: Record<string, ApiFieldError> = {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user