@12-apps/payments-frontend 3.13.0 → 3.14.0
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/package.json +4 -3
- package/src/card/en-US.ts +61 -0
- package/src/card/index.ts +1 -0
- package/src/components/checkout/en-US.ts +95 -0
- package/src/components/checkout/screens-en-US.ts +113 -0
- package/src/components/checkout-payment-en-US.ts +53 -0
- package/src/components/platform/en-US.ts +93 -0
- package/src/components/settings-en-US.ts +187 -0
- package/src/locales.ts +100 -0
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@12-apps/payments-frontend",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.14.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Browser half of the vendor-agnostic payments platform: plug-and-play MUI components for the per-provider settings page (credential form from each provider's schema, masked hints, verify/enable) and the checkout page (PIX QR + polling, card tokenization, hosted-checkout redirect), plus the headless hooks and fetch clients they build on. Talks only to the host's payments HTTP surface — never to a provider directly. Microfrontend-ready: no app coupling, host injects theme and auth.",
|
|
6
6
|
"exports": {
|
|
7
|
-
".": "./src/index.ts"
|
|
7
|
+
".": "./src/index.ts",
|
|
8
|
+
"./locales": "./src/locales.ts"
|
|
8
9
|
},
|
|
9
10
|
"scripts": {
|
|
10
11
|
"clean": "rm -rf node_modules coverage storybook-static",
|
|
@@ -17,7 +18,7 @@
|
|
|
17
18
|
"storybook:build": "storybook build"
|
|
18
19
|
},
|
|
19
20
|
"dependencies": {
|
|
20
|
-
"@12-apps/payments-backend": "^4.
|
|
21
|
+
"@12-apps/payments-backend": "^4.19.0",
|
|
21
22
|
"react-qr-code": "^2.2.0"
|
|
22
23
|
},
|
|
23
24
|
"peerDependencies": {
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { CardCopy } from './copy';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The en-US pack for the card form — a NAMED constant a host passes by hand,
|
|
5
|
+
* never a default.
|
|
6
|
+
*
|
|
7
|
+
* `MM/AA` becomes `MM/YY` and `CVV` stays `CVV`, and the difference between
|
|
8
|
+
* those two is the rule: the expiry mask is what the buyer TYPES, so it follows
|
|
9
|
+
* their own notation, while CVV is what is PRINTED on the card and is the same
|
|
10
|
+
* three letters in either language. `CPF` stays too — it is Brazil's taxpayer
|
|
11
|
+
* number, and there is nothing else to call the field a Brazilian buyer is
|
|
12
|
+
* filling in.
|
|
13
|
+
*/
|
|
14
|
+
export const EN_US_CARD_COPY: CardCopy = {
|
|
15
|
+
fields: {
|
|
16
|
+
unknownBrand: 'Card',
|
|
17
|
+
numberLabel: 'Card number',
|
|
18
|
+
numberRequired: 'Enter the card number.',
|
|
19
|
+
numberIncomplete: 'That card number is incomplete.',
|
|
20
|
+
numberInvalid: 'That card number is not valid.',
|
|
21
|
+
holderLabel: 'Name printed on the card',
|
|
22
|
+
holderRequired: 'Enter the name printed on the card.',
|
|
23
|
+
expiryLabel: 'Expiry (MM/YY)',
|
|
24
|
+
expiryPlaceholder: 'MM/YY',
|
|
25
|
+
cvvLabel: 'CVV',
|
|
26
|
+
expiryIncomplete: 'The expiry is incomplete (MM/YY).',
|
|
27
|
+
monthInvalid: 'That month is not valid.',
|
|
28
|
+
expired: 'That card has expired.',
|
|
29
|
+
expiryInvalid: 'That expiry is not valid, or has passed.',
|
|
30
|
+
cvvRequired: 'Enter the CVV.',
|
|
31
|
+
cvvInvalid: 'That CVV is not valid.',
|
|
32
|
+
// The length differs by brand (3 for most, 4 for Amex), so it is
|
|
33
|
+
// interpolated rather than written out.
|
|
34
|
+
cvvDigits: (length) => `The CVV must be ${length} digits.`,
|
|
35
|
+
cpfRequired: 'CPF is required.',
|
|
36
|
+
cpfInvalid: 'That CPF is not valid.',
|
|
37
|
+
savedCardsLabel: 'Card',
|
|
38
|
+
savedCardExpiry: (month, year) => `Expires ${month}/${year}`,
|
|
39
|
+
newCard: 'New card',
|
|
40
|
+
newCardDescription: 'Enter another card',
|
|
41
|
+
saveCard: 'Save this card for next time',
|
|
42
|
+
},
|
|
43
|
+
tokenize: {
|
|
44
|
+
sdkUnavailable: 'Could not load the payment method. Reload the page.',
|
|
45
|
+
cardNotProcessed: 'Could not process the card. Check the details and try again.',
|
|
46
|
+
providerUnreachable: 'Could not reach the card provider. Check your connection.',
|
|
47
|
+
providerTimedOut: 'The card provider did not answer in time.',
|
|
48
|
+
// The status and the raw response ride along because this is what a store
|
|
49
|
+
// owner forwards to support; a tidier sentence would drop the only part
|
|
50
|
+
// that identifies the failure.
|
|
51
|
+
providerRefused: (status, response) =>
|
|
52
|
+
`The card provider refused the card details (HTTP ${status}). ` + `Response: ${response}`,
|
|
53
|
+
noPublicKey:
|
|
54
|
+
'The card public key is not available for this store. ' +
|
|
55
|
+
'Reconnect the provider and try again.',
|
|
56
|
+
// Ends with what the BUYER can do, in order of how likely each is to work:
|
|
57
|
+
// this is the one message here a shopper reads mid-purchase.
|
|
58
|
+
cardUnavailable:
|
|
59
|
+
'Card payment is unavailable in this store right now. Reload the page and try again, choose another payment method, or arrange it with the store directly.',
|
|
60
|
+
},
|
|
61
|
+
};
|
package/src/card/index.ts
CHANGED
|
@@ -42,3 +42,4 @@ export {
|
|
|
42
42
|
export { NEW_CARD, type CardDetails, type CardFieldErrors, type CardToken, type SavedCard } from "./types";
|
|
43
43
|
export type { CardCopy, CardFieldCopy, CardTokenizeCopy } from "./copy";
|
|
44
44
|
export { PT_BR_CARD_COPY } from "./pt-BR";
|
|
45
|
+
export { EN_US_CARD_COPY } from "./en-US";
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { EN_US_CARD_COPY } from "../../card/en-US";
|
|
2
|
+
import { EN_US_CHECKOUT_SCREENS_COPY } from "./screens-en-US";
|
|
3
|
+
import type { CheckoutCopy } from "./copy-context";
|
|
4
|
+
import type { CheckoutViewCopy, PaymentStatusCopy } from "./view-copy";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The en-US packs for the checkout views — NAMED exports a host passes by hand,
|
|
8
|
+
* never defaults.
|
|
9
|
+
*
|
|
10
|
+
* Four of these sentences carry a promise rather than a description, and the
|
|
11
|
+
* translation keeps each one FIRST in its block, because the fear on this
|
|
12
|
+
* screen is having been charged for an order that failed:
|
|
13
|
+
*
|
|
14
|
+
* - `failed.support` and `expired.support` both open by saying nothing was
|
|
15
|
+
* charged;
|
|
16
|
+
* - `awaitingTimedOut.support` says "do not pay again" before anything else it
|
|
17
|
+
* has to say, because a second payment is the expensive mistake here;
|
|
18
|
+
* - `dados.secureNotice` is the one reassurance on the details step.
|
|
19
|
+
*/
|
|
20
|
+
export const EN_US_PAYMENT_STATUS_COPY: PaymentStatusCopy = {
|
|
21
|
+
paid: {
|
|
22
|
+
heading: "Order confirmed",
|
|
23
|
+
support: "We have your payment and the order is recorded.",
|
|
24
|
+
},
|
|
25
|
+
awaiting: {
|
|
26
|
+
heading: "Confirming your payment",
|
|
27
|
+
support: "This usually takes a few seconds. You can leave this screen open.",
|
|
28
|
+
},
|
|
29
|
+
failed: {
|
|
30
|
+
heading: "Payment not completed",
|
|
31
|
+
support: "Nothing was charged. You can try again.",
|
|
32
|
+
},
|
|
33
|
+
expired: {
|
|
34
|
+
heading: "The code expired",
|
|
35
|
+
support: "Nothing was charged. Generate a new code to carry on.",
|
|
36
|
+
},
|
|
37
|
+
awaitingTimedOut: {
|
|
38
|
+
heading: "We have not had the confirmation yet",
|
|
39
|
+
support:
|
|
40
|
+
"If you have already paid, the order is confirmed as soon as the provider tells us — " +
|
|
41
|
+
"do not pay again. You can close this screen.",
|
|
42
|
+
},
|
|
43
|
+
retryAction: "Try again",
|
|
44
|
+
regenerateAction: "Generate a new code",
|
|
45
|
+
backAction: "Back to the menu",
|
|
46
|
+
amountLabel: "Amount paid",
|
|
47
|
+
referenceLabel: "Order",
|
|
48
|
+
receiptEmailLabel: "Receipt sent to",
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The words the checkout's deeper screens read from context — the card form,
|
|
53
|
+
* its tokenizers, and the buyer-details step's own fields.
|
|
54
|
+
*/
|
|
55
|
+
export const EN_US_CHECKOUT_COPY: CheckoutCopy = {
|
|
56
|
+
card: EN_US_CARD_COPY,
|
|
57
|
+
screens: EN_US_CHECKOUT_SCREENS_COPY,
|
|
58
|
+
buyer: {
|
|
59
|
+
emailInvalid: "That e-mail address is not valid.",
|
|
60
|
+
emailRequired: "E-mail is required.",
|
|
61
|
+
nameRequired: "Name is required.",
|
|
62
|
+
phoneRequired: "Phone is required.",
|
|
63
|
+
// The list of REQUIRED field names is the host's configuration, so the
|
|
64
|
+
// sentence is built around it — and it inflects on how many there are,
|
|
65
|
+
// which is why this is a function rather than a template.
|
|
66
|
+
fieldsHint: (names) =>
|
|
67
|
+
names.length === 0
|
|
68
|
+
? "Name, e-mail and phone are optional — they are only used for the receipt."
|
|
69
|
+
: `Enter your ${names.join(", ")} (${names.length === 1 ? "required" : "required"} ` +
|
|
70
|
+
"for payment). The other fields are optional — they are only used for the receipt.",
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const EN_US_CHECKOUT_VIEW_COPY: CheckoutViewCopy = {
|
|
75
|
+
screens: EN_US_CHECKOUT_COPY,
|
|
76
|
+
// The step KEYS are the package's own ids; only the labels are words.
|
|
77
|
+
steps: {
|
|
78
|
+
dados: "Details",
|
|
79
|
+
payment: "Payment",
|
|
80
|
+
status: "Confirmation",
|
|
81
|
+
},
|
|
82
|
+
dados: {
|
|
83
|
+
saveProfile: "Save my details for next time",
|
|
84
|
+
cannotContinueTitle: "Could not continue",
|
|
85
|
+
continueAction: "Continue",
|
|
86
|
+
secureNotice: "Secure payment",
|
|
87
|
+
keepShopping: "Keep shopping",
|
|
88
|
+
back: "Back",
|
|
89
|
+
},
|
|
90
|
+
emptyCart: {
|
|
91
|
+
title: "Your cart is empty.",
|
|
92
|
+
action: "See the menu",
|
|
93
|
+
},
|
|
94
|
+
status: EN_US_PAYMENT_STATUS_COPY,
|
|
95
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { CheckoutScreensCopy } from './screens-copy';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The en-US pack for the buyer's checkout screens — a NAMED constant a host
|
|
5
|
+
* passes by hand, never a default.
|
|
6
|
+
*
|
|
7
|
+
* Two entries here are LOCALE TAGS rather than words, and they move with the
|
|
8
|
+
* pack because they decide how the same screen renders a time and a wallet
|
|
9
|
+
* button: `pix.expiryLocale` formats the "valid until" clock, and
|
|
10
|
+
* `wallet.googlePay.buttonLocale` is the language Google's own button renders
|
|
11
|
+
* itself in. A pack whose sentences were English and whose clock was
|
|
12
|
+
* Portuguese would be a screen written for nobody.
|
|
13
|
+
*
|
|
14
|
+
* PIX, CPF, Apple Pay and Google Pay all keep their names: the first two are
|
|
15
|
+
* Brazilian schemes the buyer will look for by name in their banking app, and
|
|
16
|
+
* the last two are the vendors' own product names, rendered by the vendors'
|
|
17
|
+
* own buttons.
|
|
18
|
+
*/
|
|
19
|
+
export const EN_US_CHECKOUT_SCREENS_COPY: CheckoutScreensCopy = {
|
|
20
|
+
method: {
|
|
21
|
+
groupLabel: 'Payment method',
|
|
22
|
+
pixLabel: 'PIX',
|
|
23
|
+
cardLabel: 'Card',
|
|
24
|
+
pixDescription: 'Approved instantly',
|
|
25
|
+
cardDescription: 'Credit, paid in full',
|
|
26
|
+
unavailableHere: 'Unavailable in this store',
|
|
27
|
+
},
|
|
28
|
+
settling: {
|
|
29
|
+
cannotConfirm: 'Could not confirm the payment',
|
|
30
|
+
takingLonger: 'The payment is taking longer than expected',
|
|
31
|
+
// "do not make another payment" is the load-bearing half: a second payment
|
|
32
|
+
// is the expensive mistake on this screen.
|
|
33
|
+
takingLongerHelp:
|
|
34
|
+
'You can wait, or check your order again shortly — do not make another payment.',
|
|
35
|
+
processing: 'Processing payment…',
|
|
36
|
+
confirming: 'We are confirming your payment',
|
|
37
|
+
cannotPay: 'Could not pay',
|
|
38
|
+
},
|
|
39
|
+
pix: {
|
|
40
|
+
heading: 'Pay with PIX',
|
|
41
|
+
instructions: (totalLabel) =>
|
|
42
|
+
`Scan the QR code in your banking app, or copy the code. Total ${totalLabel}.`,
|
|
43
|
+
qrAlt: 'PIX QR code for payment',
|
|
44
|
+
copyAction: 'Copy',
|
|
45
|
+
copiedAction: 'Copied!',
|
|
46
|
+
validUntil: (time) => `Valid until ${time}. Confirmation is automatic.`,
|
|
47
|
+
expiryLocale: 'en-US',
|
|
48
|
+
awaiting: 'Waiting for payment…',
|
|
49
|
+
chargeMissing: 'Could not generate the PIX code.',
|
|
50
|
+
},
|
|
51
|
+
card: {
|
|
52
|
+
heading: 'Pay by card',
|
|
53
|
+
},
|
|
54
|
+
payer: {
|
|
55
|
+
taxId: (formatted) => `CPF ${formatted}`,
|
|
56
|
+
taxIdAlreadyKnown: 'CPF already on file',
|
|
57
|
+
payingAs: (name) => `Paying as ${name}`,
|
|
58
|
+
payingWithSavedDetails: 'Paying with your saved details',
|
|
59
|
+
changeAction: 'Change',
|
|
60
|
+
},
|
|
61
|
+
error: {
|
|
62
|
+
confirming: 'We are confirming your payment',
|
|
63
|
+
cannotContinue: 'Could not continue',
|
|
64
|
+
retryAction: 'Try again',
|
|
65
|
+
emailLabel: 'E-mail for the payment',
|
|
66
|
+
// Lower-case and fragmentary: it renders as a hint under the field.
|
|
67
|
+
emailMustDifferHint: "use an e-mail address different from the store's",
|
|
68
|
+
useEmailAction: 'Use this e-mail and continue',
|
|
69
|
+
},
|
|
70
|
+
wallet: {
|
|
71
|
+
applePay: {
|
|
72
|
+
orderTotal: 'Order total',
|
|
73
|
+
cannotStart: 'Could not start Apple Pay in this store. Pay by card instead.',
|
|
74
|
+
cannotComplete: 'Could not start Apple Pay. Try again, or pay by card.',
|
|
75
|
+
payAction: 'Pay with Apple Pay',
|
|
76
|
+
},
|
|
77
|
+
googlePay: {
|
|
78
|
+
cannotComplete: 'Could not complete the payment with Google Pay. Try again, or pay by card.',
|
|
79
|
+
// Google's button renders itself in this language; it takes a bare
|
|
80
|
+
// language subtag, not a full BCP-47 tag.
|
|
81
|
+
buttonLocale: 'en',
|
|
82
|
+
},
|
|
83
|
+
orPayWithCard: 'or pay by card',
|
|
84
|
+
},
|
|
85
|
+
hosted: {
|
|
86
|
+
// Five fragments the screen composes into one sentence, so each keeps its
|
|
87
|
+
// leading preposition and none reads as a sentence on its own.
|
|
88
|
+
destinationNamed: (displayName) => `to ${displayName}'s payment page`,
|
|
89
|
+
destinationGeneric: "to the provider's secure payment page",
|
|
90
|
+
methodsChoice: (methods) => `, where you choose to pay with ${methods}`,
|
|
91
|
+
pixAndCard: 'PIX or card',
|
|
92
|
+
pixOnly: 'PIX',
|
|
93
|
+
cardOnly: 'card',
|
|
94
|
+
handoff: (destination, choice) => `You will be taken ${destination}${choice}.`,
|
|
95
|
+
afterwards:
|
|
96
|
+
'Once the payment goes through, you come back here and we confirm the order.',
|
|
97
|
+
startAction: 'Continue to payment',
|
|
98
|
+
preparing: 'Preparing the payment',
|
|
99
|
+
},
|
|
100
|
+
transport: {
|
|
101
|
+
failed: 'Could not complete the operation. Try again.',
|
|
102
|
+
invalidResponse: 'Invalid response from the server.',
|
|
103
|
+
offline: 'Could not connect. Check your connection and try again.',
|
|
104
|
+
},
|
|
105
|
+
validation: {
|
|
106
|
+
taxIdInvalid: 'That CPF is not valid.',
|
|
107
|
+
nameRequired: 'Enter your name.',
|
|
108
|
+
emailInvalid: 'That e-mail address is not valid.',
|
|
109
|
+
phoneInvalid: 'That phone number is not valid.',
|
|
110
|
+
required: 'This field is required.',
|
|
111
|
+
},
|
|
112
|
+
generatingPayment: 'Generating payment…',
|
|
113
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { CheckoutPaymentCopy } from './checkout-payment-copy';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The en-US pack for the legacy `CheckoutPayment` step — a NAMED constant a
|
|
5
|
+
* host passes by hand, never a default.
|
|
6
|
+
*
|
|
7
|
+
* `money.amountLocale` is part of the pack and moves with it: it is what the
|
|
8
|
+
* step formats the total with, so the words around a price and the price
|
|
9
|
+
* itself are written for the same reader. It does NOT change the CURRENCY —
|
|
10
|
+
* an English-reading buyer of a Brazilian store still pays in BRL.
|
|
11
|
+
*
|
|
12
|
+
* PIX keeps its name. It is Brazil's instant-payment scheme, and the buyer will
|
|
13
|
+
* look for those three letters in their banking app.
|
|
14
|
+
*/
|
|
15
|
+
export const EN_US_CHECKOUT_PAYMENT_COPY: CheckoutPaymentCopy = {
|
|
16
|
+
money: {
|
|
17
|
+
totalLabel: (formattedAmount) => `Total: ${formattedAmount}`,
|
|
18
|
+
payAction: (formattedAmount) => `Pay ${formattedAmount}`,
|
|
19
|
+
amountLocale: 'en-US',
|
|
20
|
+
},
|
|
21
|
+
method: {
|
|
22
|
+
groupLabel: 'Payment method',
|
|
23
|
+
pixTitle: 'PIX',
|
|
24
|
+
pixSubtitle: 'Approved instantly',
|
|
25
|
+
cardTitle: 'Card',
|
|
26
|
+
cardSubtitle: 'Credit, paid in full',
|
|
27
|
+
generatePixAction: 'Generate a PIX QR code',
|
|
28
|
+
continueToPaymentAction: 'Continue to payment',
|
|
29
|
+
},
|
|
30
|
+
pix: {
|
|
31
|
+
qrAlt: 'PIX QR code',
|
|
32
|
+
copyPasteLabel: 'PIX copy-and-paste code',
|
|
33
|
+
copyAction: 'Copy the code',
|
|
34
|
+
copiedAction: 'Copied!',
|
|
35
|
+
awaiting: 'Waiting for payment…',
|
|
36
|
+
},
|
|
37
|
+
card: {
|
|
38
|
+
heading: 'Pay by card',
|
|
39
|
+
numberLabel: 'Card number',
|
|
40
|
+
holderLabel: 'Name printed on the card',
|
|
41
|
+
expiryLabel: 'Expiry (MM/YY)',
|
|
42
|
+
cvvLabel: 'CVV',
|
|
43
|
+
payAction: 'Pay by card',
|
|
44
|
+
newCard: 'New card — enter another card',
|
|
45
|
+
savedCard: (brand, last4, expiry) =>
|
|
46
|
+
`${brand} •••• ${last4}${expiry ? ` — expires ${expiry}` : ''}`,
|
|
47
|
+
},
|
|
48
|
+
refusal: {
|
|
49
|
+
paymentsOff: 'This store does not accept online payments yet.',
|
|
50
|
+
cardUnavailable: 'Card payment is unavailable.',
|
|
51
|
+
redirectNotice: 'You will be taken somewhere secure to finish the payment.',
|
|
52
|
+
},
|
|
53
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { PlatformHomologacaoCopy } from './copy';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The en-US pack for the platform's homologação screens.
|
|
5
|
+
*
|
|
6
|
+
* A NAMED pack, which is how this repo ships a language: a host imports it and
|
|
7
|
+
* passes it by hand.
|
|
8
|
+
*
|
|
9
|
+
* ## What is translated here, and what is not
|
|
10
|
+
*
|
|
11
|
+
* These screens are read by the platform OPERATOR filling PagBank's form, and
|
|
12
|
+
* that is what the words are for — so the instructions, headings and statuses
|
|
13
|
+
* are English here.
|
|
14
|
+
*
|
|
15
|
+
* What stays is everything the operator has to MATCH against PagBank's own
|
|
16
|
+
* surfaces: `redirect_uri` is the parameter name in the API response, "SIP" and
|
|
17
|
+
* "Pipefy" are the names of the systems they will open, `ACCESS_DENIED` is the
|
|
18
|
+
* error string they are quoting in the ticket, and "homologação" is what
|
|
19
|
+
* PagBank calls the process — an operator searching support for
|
|
20
|
+
* "homologation" finds nothing.
|
|
21
|
+
*
|
|
22
|
+
* The ANSWERS the form is filled with are a different matter and are not here
|
|
23
|
+
* at all: they live in `@12-apps/payments-backend`'s `PT_BR_HOMOLOGACAO_ANSWERS`
|
|
24
|
+
* and stay Portuguese, because a PagBank reviewer reads them and several are
|
|
25
|
+
* multiple-choice options on the form itself.
|
|
26
|
+
*/
|
|
27
|
+
export const EN_US_PLATFORM_HOMOLOGACAO_COPY: PlatformHomologacaoCopy = {
|
|
28
|
+
outcome: {
|
|
29
|
+
heading: 'Homologação status',
|
|
30
|
+
statusLabel: 'Status',
|
|
31
|
+
notSubmitted: 'Not submitted',
|
|
32
|
+
protocolLabel: 'Protocol',
|
|
33
|
+
protocolPlaceholder: 'Protocol (Pipefy card / ticket)',
|
|
34
|
+
notesLabel: 'Notes',
|
|
35
|
+
notesPlaceholder: "Notes (PagBank's reply, context…)",
|
|
36
|
+
save: 'Record',
|
|
37
|
+
saved: 'Record updated.',
|
|
38
|
+
// The KEYS are the stored status values, not words.
|
|
39
|
+
statuses: { SUBMITTED: 'Submitted', APPROVED: 'Approved', REJECTED: 'Rejected' },
|
|
40
|
+
// Three fragments the screen concatenates into one line, so the first two
|
|
41
|
+
// keep their trailing space and the last one ends the sentence.
|
|
42
|
+
submittedAt: (when) => `Submitted ${when}. `,
|
|
43
|
+
decidedAt: (when) => `Decided ${when}. `,
|
|
44
|
+
recordedBy: (who) => `Recorded by ${who}.`,
|
|
45
|
+
},
|
|
46
|
+
guide: {
|
|
47
|
+
heading: 'Homologação form — answers ready to paste',
|
|
48
|
+
// Five fragments wrapping two links. Each keeps its own leading or
|
|
49
|
+
// trailing space; a translation that made them whole sentences would break
|
|
50
|
+
// the line the screen actually renders.
|
|
51
|
+
ledeBeforeForm: 'Open the ',
|
|
52
|
+
formLink: 'official homologação form',
|
|
53
|
+
ledeBeforeSupport: ' and fill it in with the values below. In parallel, open a ticket on ',
|
|
54
|
+
supportLink: 'SIP — PagBank integration support',
|
|
55
|
+
ledeBeforeDocs:
|
|
56
|
+
' quoting the 403 ACCESS_DENIED: what you answer first decides whether the form covers Connect. Documentation: ',
|
|
57
|
+
docsLink: 'requesting homologação',
|
|
58
|
+
ledeAfterDocs: '.',
|
|
59
|
+
},
|
|
60
|
+
anexo: {
|
|
61
|
+
heading: 'Evidence attachment',
|
|
62
|
+
body:
|
|
63
|
+
"The form asks for the requests and responses of the calls sent to PagBank's APIs. The file is generated from this platform's real calls, with the token redacted.",
|
|
64
|
+
generate: 'Generate attachment',
|
|
65
|
+
generateFailed: 'Could not generate the attachment.',
|
|
66
|
+
},
|
|
67
|
+
connect: {
|
|
68
|
+
expectedRedirectHeading: 'The callback this deploy uses (the value that must be registered)',
|
|
69
|
+
consultAgain: 'Look it up again',
|
|
70
|
+
noApplication: 'No application configured in this environment.',
|
|
71
|
+
// `redirect_uri` is the parameter name in PagBank's own response — an
|
|
72
|
+
// operator compares the two strings character by character.
|
|
73
|
+
redirectMatches: 'The registered redirect_uri matches the callback this deploy uses.',
|
|
74
|
+
redirectDiffers:
|
|
75
|
+
'The redirect_uri registered with PagBank differs from the callback this deploy uses.',
|
|
76
|
+
redirectUnreported:
|
|
77
|
+
"PagBank's response carried no redirect_uri, so it could not be compared with the callback.",
|
|
78
|
+
fields: {
|
|
79
|
+
name: 'Name (shown to the store owner)',
|
|
80
|
+
site: 'Site',
|
|
81
|
+
description: 'Description',
|
|
82
|
+
logo: 'Logo',
|
|
83
|
+
redirectUri: 'registered redirect_uri',
|
|
84
|
+
},
|
|
85
|
+
fieldEmpty: '—',
|
|
86
|
+
redirectNotReported: 'not reported',
|
|
87
|
+
resolvedFrom:
|
|
88
|
+
"This environment's application is resolved strictly from these variables, with no fallback between environments:",
|
|
89
|
+
showConfig: 'Show environment variables',
|
|
90
|
+
hideConfig: 'Hide environment variables',
|
|
91
|
+
extraKeys: 'Other fields returned',
|
|
92
|
+
},
|
|
93
|
+
};
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import type { PaymentsSettingsCopy } from './settings-copy';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The en-US pack for the payments settings surface — a NAMED constant a host
|
|
5
|
+
* passes by hand, never a default.
|
|
6
|
+
*
|
|
7
|
+
* The four sentences that matter most on this screen are the ones about MONEY
|
|
8
|
+
* MOVING, and the translation keeps each of them blunt rather than polite:
|
|
9
|
+
*
|
|
10
|
+
* - `confirmSave.warning` — a wrong value pays a stranger, irreversibly;
|
|
11
|
+
* - `card.removeConsequenceLive` — removing a live connection stops the store
|
|
12
|
+
* taking money immediately;
|
|
13
|
+
* - `priority.noneActive` — with nothing active the checkout cannot charge;
|
|
14
|
+
* - `credentials.reverifyWarning` — changing credentials stops collection
|
|
15
|
+
* until re-verification finishes.
|
|
16
|
+
*
|
|
17
|
+
* `**bold**` markers are the surface's own emphasis and survive translation, on
|
|
18
|
+
* the phrase that carries the consequence.
|
|
19
|
+
*/
|
|
20
|
+
export const EN_US_PAYMENTS_SETTINGS_COPY: PaymentsSettingsCopy = {
|
|
21
|
+
status: {
|
|
22
|
+
verified: 'VERIFIED',
|
|
23
|
+
unverified: 'NOT VERIFIED',
|
|
24
|
+
connectionOk: 'CONNECTION OK',
|
|
25
|
+
reconnectRequired: 'RECONNECT',
|
|
26
|
+
notConnected: 'Not connected',
|
|
27
|
+
threeStepsAhead: 'This provider only starts taking sales after the 3 steps below.',
|
|
28
|
+
connectAndVerifyFirst: 'Connect and verify the provider before switching sales on.',
|
|
29
|
+
receiving: {
|
|
30
|
+
state: 'Taking sales',
|
|
31
|
+
sub: 'Your store is being paid through this provider.',
|
|
32
|
+
},
|
|
33
|
+
pausedByOwner: {
|
|
34
|
+
state: 'Paused',
|
|
35
|
+
sub: 'Connection ready and paused by you — no new order is charged here.',
|
|
36
|
+
},
|
|
37
|
+
pausedChip: 'Paused',
|
|
38
|
+
readyNotReceiving: {
|
|
39
|
+
state: 'Not taking sales yet',
|
|
40
|
+
sub: 'Everything is ready — flip the switch to start taking payments.',
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
listBadge: {
|
|
44
|
+
active: 'Active',
|
|
45
|
+
connected: 'Connected',
|
|
46
|
+
reconnect: 'Reconnect',
|
|
47
|
+
notConnected: 'Not connected',
|
|
48
|
+
},
|
|
49
|
+
environment: {
|
|
50
|
+
production: 'Production',
|
|
51
|
+
sandbox: 'Sandbox',
|
|
52
|
+
groupLabel: "This connection's environment",
|
|
53
|
+
productionMeaning: 'Production — real money.',
|
|
54
|
+
sandboxMeaning: 'Sandbox — a test environment.',
|
|
55
|
+
productionConsequence: "Everything you do here applies to the store's real sales.",
|
|
56
|
+
sandboxConsequence: 'No money actually moves.',
|
|
57
|
+
// Leading space: it is appended to one of the sentences above.
|
|
58
|
+
storeIsUsing: (environmentName) => ` The store is currently using ${environmentName}.`,
|
|
59
|
+
},
|
|
60
|
+
oauth: {
|
|
61
|
+
connectAction: (displayName) => `Connect with ${displayName}`,
|
|
62
|
+
reconnectAction: 'Reconnect',
|
|
63
|
+
removeAction: 'Remove connection',
|
|
64
|
+
invitation: (displayName) =>
|
|
65
|
+
`Connect your ${displayName} account by authorising access on the provider's site. No keys to copy.`,
|
|
66
|
+
roundTripNote: 'You go out to the provider and come back here — it takes under a minute.',
|
|
67
|
+
connectedExplainer:
|
|
68
|
+
'Your account is connected. Charges are created on your behalf — no keys to copy.',
|
|
69
|
+
connectedNote: (displayName) =>
|
|
70
|
+
`${displayName} account connected. Revoke it whenever you like, here or in the provider's dashboard.`,
|
|
71
|
+
connectedAt: (when) => `Connected ${when}`,
|
|
72
|
+
validUntil: (when) => `Authorisation valid until ${when}`,
|
|
73
|
+
expiredAt: (when) =>
|
|
74
|
+
`The authorisation expired on ${when}. Reconnect to start taking payments again.`,
|
|
75
|
+
expiresAt: (when) =>
|
|
76
|
+
`The authorisation expires on ${when}. If the warning stays, reconnect the account.`,
|
|
77
|
+
revoked:
|
|
78
|
+
'The authorisation expired or was revoked. Reconnect to start taking payments again.',
|
|
79
|
+
notAvailableHere: (displayName) =>
|
|
80
|
+
`Connecting to ${displayName} automatically is not available in this installation — ` +
|
|
81
|
+
'the authorisation application has not been registered. To connect now, open ' +
|
|
82
|
+
'“I would rather enter the credentials myself” below and paste your own keys.',
|
|
83
|
+
connectUnavailable:
|
|
84
|
+
'This provider connects by authorisation, but the connect button is not available in this ' +
|
|
85
|
+
'installation. You can still connect by entering the credentials yourself.',
|
|
86
|
+
preferOAuth: 'I would rather connect by authorisation',
|
|
87
|
+
preferCredentials: 'I would rather enter the credentials myself',
|
|
88
|
+
// What the owner is granting, shown before they authorise.
|
|
89
|
+
scopes: {
|
|
90
|
+
read: 'Look up payments',
|
|
91
|
+
create: 'Create charges',
|
|
92
|
+
refund: 'Refund payments',
|
|
93
|
+
account: 'Look up account details',
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
card: {
|
|
97
|
+
accountHeading: (displayName) => `${displayName} account`,
|
|
98
|
+
accountLabel: 'Account',
|
|
99
|
+
connectionLabel: 'Connection',
|
|
100
|
+
authorizedAt: (displayName) => `Authorised on ${displayName}`,
|
|
101
|
+
environmentLabel: 'Environment',
|
|
102
|
+
connectedAtLabel: 'Connected',
|
|
103
|
+
sandboxWithNote: 'Sandbox (testing)',
|
|
104
|
+
steps: {
|
|
105
|
+
signIn: (displayName) =>
|
|
106
|
+
`Sign in to your ${displayName} account — you can create one on the spot if you do not have one.`,
|
|
107
|
+
authorize: "Authorise access on the provider's screen.",
|
|
108
|
+
comeBack: 'You come back here with the account connected.',
|
|
109
|
+
},
|
|
110
|
+
removeAction: 'Remove connection',
|
|
111
|
+
removeQuestion: (displayName) => `Remove the connection to ${displayName}?`,
|
|
112
|
+
removeConsequenceLive:
|
|
113
|
+
'The store stops taking payments immediately and is left with no active provider — new orders cannot be paid until you connect another.',
|
|
114
|
+
removeConsequenceIdle: 'This connection leaves the store. You can connect again later.',
|
|
115
|
+
removeStopsChargingNow: 'New orders stop being charged immediately.',
|
|
116
|
+
removeRevokes: (displayName) =>
|
|
117
|
+
`The authorisation is revoked at ${displayName}. Your account and its history stay there, untouched.`,
|
|
118
|
+
removeKeepsSettled: (displayName) =>
|
|
119
|
+
`Payments already approved and refunds in flight carry on as normal through ${displayName}.`,
|
|
120
|
+
removeRestartsSetup: 'To reconnect, the steps start again from the beginning.',
|
|
121
|
+
pauseInstead: 'Just pause taking payments',
|
|
122
|
+
cancel: 'Cancel',
|
|
123
|
+
},
|
|
124
|
+
credentials: {
|
|
125
|
+
configuredKeepBlank: 'Configured — leave blank to keep the current value.',
|
|
126
|
+
// Leading space: it is appended to a field label.
|
|
127
|
+
advancedSuffix: ' · Connect platforms only',
|
|
128
|
+
probeAction: 'Test connection',
|
|
129
|
+
reverifyWarning: (displayName) =>
|
|
130
|
+
`This store is already verified. Changing the credentials **requires re-verification** ` +
|
|
131
|
+
`and the store **stops taking payments** through ${displayName} until it finishes.`,
|
|
132
|
+
probeRunning: 'Testing the connection…',
|
|
133
|
+
probeSaveNote: 'We save and test the keys with the provider before going on.',
|
|
134
|
+
probeIncompleteNote: 'We kept what you have filled in. Complete the fields to test.',
|
|
135
|
+
probeFailed: (environmentName) =>
|
|
136
|
+
`Could not connect in ${environmentName}. Check the credentials for this environment.`,
|
|
137
|
+
checkPass: 'Verified',
|
|
138
|
+
checkFail: 'Fix',
|
|
139
|
+
uncheckable: 'Not verifiable',
|
|
140
|
+
saveAndTest: 'Save and test connection',
|
|
141
|
+
save: 'Save',
|
|
142
|
+
saveOnly: (fieldLabel) => `Save ${fieldLabel}`,
|
|
143
|
+
changeAction: 'Change',
|
|
144
|
+
},
|
|
145
|
+
priority: {
|
|
146
|
+
moveUp: (label) => `Move ${label} up`,
|
|
147
|
+
moveDown: (label) => `Move ${label} down`,
|
|
148
|
+
saveFailed: 'Could not save the order.',
|
|
149
|
+
firstInChain: 'first',
|
|
150
|
+
orderHeading: 'Order of attempts',
|
|
151
|
+
chainExplainer: (firstProviderLabel) =>
|
|
152
|
+
`Checkout tries **${firstProviderLabel}** first. If a charge fails for a technical ` +
|
|
153
|
+
'reason it tries the next in the list — but only when it can prove the previous ' +
|
|
154
|
+
'attempt did not charge anything.',
|
|
155
|
+
noneActive:
|
|
156
|
+
'No provider is active. Checkout will not be able to charge until you switch at least one on.',
|
|
157
|
+
retryDeclinedLabel: 'Retry a **declined** card with the next provider',
|
|
158
|
+
// Both sides of the switch say what it COSTS, which is the point of the
|
|
159
|
+
// pair: one names the risk, the other names the default.
|
|
160
|
+
retryDeclinedOn:
|
|
161
|
+
'A decline moves on to the next in the list. This can raise transaction costs and fraud signals.',
|
|
162
|
+
retryDeclinedOff:
|
|
163
|
+
'Default: a decline ends the charge. Only technical failures move on to the next.',
|
|
164
|
+
},
|
|
165
|
+
providerList: {
|
|
166
|
+
heading: 'Choose the payment provider',
|
|
167
|
+
subheading: 'Pick where your store is paid — the setup is tailored to it.',
|
|
168
|
+
},
|
|
169
|
+
confirmSave: {
|
|
170
|
+
title: 'Confirm where the money goes',
|
|
171
|
+
body: (fieldLabel) =>
|
|
172
|
+
`Every payment this store takes will be deposited into the account this ${fieldLabel} belongs to:`,
|
|
173
|
+
fieldFallback: 'credential',
|
|
174
|
+
warning:
|
|
175
|
+
"A wrong value sends this store's payments to somebody else, and there is no way to reverse it.",
|
|
176
|
+
cancelAction: 'Go back and check',
|
|
177
|
+
confirmAction: "That's the one, save it",
|
|
178
|
+
},
|
|
179
|
+
setupGuide: {
|
|
180
|
+
defaultConfirmLabel: 'I have enabled Integrated Checkout',
|
|
181
|
+
confirmedByYou: 'Confirmed by you',
|
|
182
|
+
reviewAction: 'Review',
|
|
183
|
+
confirmPrompt: "Confirm once you have finished on the provider's side.",
|
|
184
|
+
copyValue: (fieldLabel) => `Copy ${fieldLabel}`,
|
|
185
|
+
copied: 'Copied',
|
|
186
|
+
},
|
|
187
|
+
};
|
package/src/locales.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { CardCopy } from './card/copy';
|
|
2
|
+
import { EN_US_CARD_COPY, PT_BR_CARD_COPY } from './card';
|
|
3
|
+
import type { CheckoutPaymentCopy } from './components/checkout-payment-copy';
|
|
4
|
+
import { EN_US_CHECKOUT_PAYMENT_COPY } from './components/checkout-payment-en-US';
|
|
5
|
+
import { PT_BR_CHECKOUT_PAYMENT_COPY } from './components/checkout-payment-pt-BR';
|
|
6
|
+
import type { CheckoutCopy } from './components/checkout/copy-context';
|
|
7
|
+
import {
|
|
8
|
+
EN_US_CHECKOUT_COPY,
|
|
9
|
+
EN_US_CHECKOUT_VIEW_COPY,
|
|
10
|
+
EN_US_PAYMENT_STATUS_COPY,
|
|
11
|
+
} from './components/checkout/en-US';
|
|
12
|
+
import {
|
|
13
|
+
PT_BR_CHECKOUT_COPY,
|
|
14
|
+
PT_BR_CHECKOUT_VIEW_COPY,
|
|
15
|
+
PT_BR_PAYMENT_STATUS_COPY,
|
|
16
|
+
} from './components/checkout/pt-BR';
|
|
17
|
+
import { EN_US_CHECKOUT_SCREENS_COPY } from './components/checkout/screens-en-US';
|
|
18
|
+
import type { CheckoutScreensCopy } from './components/checkout/screens-copy';
|
|
19
|
+
import { PT_BR_CHECKOUT_SCREENS_COPY } from './components/checkout/screens-pt-BR';
|
|
20
|
+
import type { CheckoutViewCopy, PaymentStatusCopy } from './components/checkout/view-copy';
|
|
21
|
+
import type { PlatformHomologacaoCopy } from './components/platform/copy';
|
|
22
|
+
import { EN_US_PLATFORM_HOMOLOGACAO_COPY } from './components/platform/en-US';
|
|
23
|
+
import { PT_BR_PLATFORM_HOMOLOGACAO_COPY } from './components/platform/pt-BR';
|
|
24
|
+
import { EN_US_PAYMENTS_SETTINGS_COPY } from './components/settings-en-US';
|
|
25
|
+
import type { PaymentsSettingsCopy } from './components/settings-copy';
|
|
26
|
+
import { PT_BR_PAYMENTS_SETTINGS_COPY } from './components/settings-pt-BR';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Every surface this half renders, in both languages, keyed by tag.
|
|
30
|
+
*
|
|
31
|
+
* The two AUDIENCES here are worth keeping apart when a host wires them: the
|
|
32
|
+
* checkout packs are read by a SHOPPER mid-purchase and the settings and
|
|
33
|
+
* homologação packs by the store's OPERATOR. A store whose owner reads English
|
|
34
|
+
* and whose buyers read Portuguese is the ordinary case, not an edge one, so
|
|
35
|
+
* these resolve from different preferences rather than one.
|
|
36
|
+
*
|
|
37
|
+
* `LocalePack` is declared here rather than imported, and in this package that
|
|
38
|
+
* is not merely a preference: `payments/no-host-imports` forbids
|
|
39
|
+
* `packages/payments/**` from importing a sibling workspace package at all.
|
|
40
|
+
*/
|
|
41
|
+
type LocalePack<T> = { readonly 'pt-BR': T; readonly 'en-US': T };
|
|
42
|
+
|
|
43
|
+
export const CARD_COPY = {
|
|
44
|
+
'pt-BR': PT_BR_CARD_COPY,
|
|
45
|
+
'en-US': EN_US_CARD_COPY,
|
|
46
|
+
} as const satisfies LocalePack<CardCopy>;
|
|
47
|
+
|
|
48
|
+
export const CHECKOUT_SCREENS_COPY = {
|
|
49
|
+
'pt-BR': PT_BR_CHECKOUT_SCREENS_COPY,
|
|
50
|
+
'en-US': EN_US_CHECKOUT_SCREENS_COPY,
|
|
51
|
+
} as const satisfies LocalePack<CheckoutScreensCopy>;
|
|
52
|
+
|
|
53
|
+
export const CHECKOUT_COPY = {
|
|
54
|
+
'pt-BR': PT_BR_CHECKOUT_COPY,
|
|
55
|
+
'en-US': EN_US_CHECKOUT_COPY,
|
|
56
|
+
} as const satisfies LocalePack<CheckoutCopy>;
|
|
57
|
+
|
|
58
|
+
export const CHECKOUT_VIEW_COPY = {
|
|
59
|
+
'pt-BR': PT_BR_CHECKOUT_VIEW_COPY,
|
|
60
|
+
'en-US': EN_US_CHECKOUT_VIEW_COPY,
|
|
61
|
+
} as const satisfies LocalePack<CheckoutViewCopy>;
|
|
62
|
+
|
|
63
|
+
export const PAYMENT_STATUS_COPY = {
|
|
64
|
+
'pt-BR': PT_BR_PAYMENT_STATUS_COPY,
|
|
65
|
+
'en-US': EN_US_PAYMENT_STATUS_COPY,
|
|
66
|
+
} as const satisfies LocalePack<PaymentStatusCopy>;
|
|
67
|
+
|
|
68
|
+
export const CHECKOUT_PAYMENT_COPY = {
|
|
69
|
+
'pt-BR': PT_BR_CHECKOUT_PAYMENT_COPY,
|
|
70
|
+
'en-US': EN_US_CHECKOUT_PAYMENT_COPY,
|
|
71
|
+
} as const satisfies LocalePack<CheckoutPaymentCopy>;
|
|
72
|
+
|
|
73
|
+
export const PAYMENTS_SETTINGS_COPY = {
|
|
74
|
+
'pt-BR': PT_BR_PAYMENTS_SETTINGS_COPY,
|
|
75
|
+
'en-US': EN_US_PAYMENTS_SETTINGS_COPY,
|
|
76
|
+
} as const satisfies LocalePack<PaymentsSettingsCopy>;
|
|
77
|
+
|
|
78
|
+
export const PLATFORM_HOMOLOGACAO_COPY = {
|
|
79
|
+
'pt-BR': PT_BR_PLATFORM_HOMOLOGACAO_COPY,
|
|
80
|
+
'en-US': EN_US_PLATFORM_HOMOLOGACAO_COPY,
|
|
81
|
+
} as const satisfies LocalePack<PlatformHomologacaoCopy>;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The en-US packs are published from HERE rather than from the root barrel,
|
|
85
|
+
* which is at its 400-line budget. That is a happier arrangement than it
|
|
86
|
+
* sounds: `@12-apps/payments-frontend/locales` is now the one subpath that
|
|
87
|
+
* holds everything about language — the per-locale packs and the records that
|
|
88
|
+
* key them — while the root keeps exporting the pt-BR names it always has, so
|
|
89
|
+
* no existing import moves.
|
|
90
|
+
*/
|
|
91
|
+
export { EN_US_CARD_COPY } from './card';
|
|
92
|
+
export { EN_US_CHECKOUT_PAYMENT_COPY } from './components/checkout-payment-en-US';
|
|
93
|
+
export {
|
|
94
|
+
EN_US_CHECKOUT_COPY,
|
|
95
|
+
EN_US_CHECKOUT_VIEW_COPY,
|
|
96
|
+
EN_US_PAYMENT_STATUS_COPY,
|
|
97
|
+
} from './components/checkout/en-US';
|
|
98
|
+
export { EN_US_CHECKOUT_SCREENS_COPY } from './components/checkout/screens-en-US';
|
|
99
|
+
export { EN_US_PAYMENTS_SETTINGS_COPY } from './components/settings-en-US';
|
|
100
|
+
export { EN_US_PLATFORM_HOMOLOGACAO_COPY } from './components/platform/en-US';
|