@pandait.tech/payment-nuvei 0.1.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/README.md ADDED
@@ -0,0 +1,211 @@
1
+ # @pandait.tech/payment-nuvei
2
+
3
+ Nuvei Ecuador payment gateway adapter for Next.js. Ships:
4
+
5
+ - **SDK wrapper** for Nuvei's REST endpoints (debit, verify, refund, card list/delete).
6
+ - **Route-handler factories** for `/api/payment/*` and `/api/webhooks/nuvei` — drop-in for Next.js App Router.
7
+ - **Payment-link primitives** (token gen, expiry helpers, anonymous-auth helper).
8
+ - **React UI components** (`CardVisual`, `SavedCards`, `NuveiPaymentForm`) with bank-color branding driven by the package's BIN/bank-colors database.
9
+
10
+ Extracted from `pauhenriques-website` (production-validated).
11
+
12
+ ## Status
13
+
14
+ V0 — Phase 1 of `panda-commerce-kit`. Published to public npm.
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ pnpm add @pandait.tech/payment-nuvei
20
+ ```
21
+
22
+ Peer deps the consumer must install:
23
+
24
+ | Peer | When required |
25
+ |---|---|
26
+ | `next` >= 14 | Always (handlers + UI use `next/server` and `next/script`) |
27
+ | `react` >= 18 | Only if importing `/ui` |
28
+ | `react-icons` >= 5 | Only if importing `/ui` |
29
+ | `zod` >= 3.22 | Always (used by 3ds-complete handler validation) |
30
+ | `firebase-admin` >= 12 | Always (handlers need Firestore + Auth admin SDKs) |
31
+ | `firebase` >= 10 | Only if importing `createAnonymousAuthHelper` |
32
+
33
+ ## Public API
34
+
35
+ ```ts
36
+ // Pure SDK functions (server-side)
37
+ import {
38
+ debitWithToken,
39
+ verifyThreeDS,
40
+ refundTransaction,
41
+ listCards,
42
+ deleteCard,
43
+ verifyCard,
44
+ } from "@pandait.tech/payment-nuvei";
45
+
46
+ // Route handler factories
47
+ import {
48
+ createChargeHandler,
49
+ createWebhookHandler,
50
+ create3dsCallbackHandler,
51
+ create3dsCompleteHandler,
52
+ create3dsTimeoutHandler,
53
+ createRefundHandler,
54
+ createInitCheckoutHandler,
55
+ createCardsHandler,
56
+ createVerifyHandler,
57
+ createTestChargeHandler,
58
+ } from "@pandait.tech/payment-nuvei/handlers";
59
+
60
+ // Payment-link primitives
61
+ import {
62
+ generatePaymentLinkToken,
63
+ defaultExpiry,
64
+ isExpired,
65
+ createAnonymousAuthHelper,
66
+ PAYMENT_LINK_PRICE_MIN,
67
+ PAYMENT_LINK_PRICE_MAX,
68
+ } from "@pandait.tech/payment-nuvei/payment-links";
69
+
70
+ // React UI components
71
+ import {
72
+ CardVisual,
73
+ SavedCards,
74
+ NuveiPaymentForm,
75
+ // utilities
76
+ getBinDatabase,
77
+ lookupBin,
78
+ lookupBankPalette,
79
+ } from "@pandait.tech/payment-nuvei/ui";
80
+ ```
81
+
82
+ ## Handler factories — usage
83
+
84
+ ```ts
85
+ // app/api/payment/charge/route.ts
86
+ import { createChargeHandler } from "@pandait.tech/payment-nuvei/handlers";
87
+ import { dbAdmin, authAdmin } from "@/lib/firebase";
88
+ import { sendPaymentConfirmation, sendPaymentFailed } from "@/lib/email";
89
+ import { getPriceDisplay } from "@/lib/pricing";
90
+
91
+ export const POST = createChargeHandler({
92
+ firebase: { db: dbAdmin, auth: authAdmin },
93
+ email: { sendPaymentConfirmation, sendPaymentFailed },
94
+ getPriceDisplay,
95
+ merchantName: "Acme Shop",
96
+ // Required for 3DS to work. Without it, browser-info is sent to Nuvei but
97
+ // 3DS challenges won't initiate — the handler logs a loud error if browserInfo
98
+ // is provided but cloudFunctionsBaseUrl is missing.
99
+ cloudFunctionsBaseUrl: process.env.CLOUD_FUNCTIONS_BASE_URL,
100
+ rateLimit: checkRateLimit, // optional
101
+ turnstile: verifyTurnstile, // optional
102
+ // Optional consumer-specific side effects after order transitions to paid
103
+ onPaymentSucceeded: async (order) => {
104
+ await ensureFulfillment(order.id);
105
+ },
106
+ // Optional retry URL for payment-failed emails
107
+ getRetryUrl: (order) => `https://shop.example.com/checkout?orderId=${order.id}`,
108
+ });
109
+ ```
110
+
111
+ Apply the same pattern to the remaining handlers — each one is documented inline with its own `XHandlerDeps` interface.
112
+
113
+ ## UI components — usage
114
+
115
+ All `/ui` exports are client components (`"use client"`) and expect a Tailwind setup. The components consume CSS variables for theming so the same components work for every white-label client with their own branding.
116
+
117
+ ### 1. Define CSS variables in your global stylesheet
118
+
119
+ Add this to your `app/globals.css` (Next.js) or equivalent, in `:root`:
120
+
121
+ ```css
122
+ :root {
123
+ /* Brand */
124
+ --color-primary: #a68a63;
125
+ --color-primary-hover: #b89a73;
126
+
127
+ /* Text */
128
+ --color-text-main: #c1c4a7;
129
+
130
+ /* Surfaces */
131
+ --color-background: #343d2a;
132
+ --color-surface-elevated: #475536;
133
+
134
+ /* Borders */
135
+ --color-border-default: rgba(193, 196, 167, 0.20);
136
+ --color-border-strong: rgba(193, 196, 167, 0.35);
137
+ --color-border-subtle: rgba(193, 196, 167, 0.12);
138
+
139
+ /* Status */
140
+ --color-error: #c75c4a;
141
+ --color-warning: #c9a84c;
142
+ }
143
+
144
+ /* Optional: dark mode palette */
145
+ .dark {
146
+ --color-primary: #b89a73;
147
+ /* ...override as needed */
148
+ }
149
+ ```
150
+
151
+ ### 2. Add the package to Tailwind's content config
152
+
153
+ Tailwind needs to detect the arbitrary-value utility classes (e.g. `bg-[var(--color-primary)]`) inside the package's compiled output:
154
+
155
+ ```js
156
+ // tailwind.config.ts
157
+ export default {
158
+ content: [
159
+ "./app/**/*.{ts,tsx}",
160
+ "./src/**/*.{ts,tsx}",
161
+ "./node_modules/@pandait.tech/payment-nuvei/dist/**/*.{js,cjs}",
162
+ ],
163
+ // ...
164
+ };
165
+ ```
166
+
167
+ ### 3. Use the components
168
+
169
+ ```tsx
170
+ "use client";
171
+ import { SavedCards, NuveiPaymentForm } from "@pandait.tech/payment-nuvei/ui";
172
+
173
+ export function PaymentMethodPicker() {
174
+ const [token, setToken] = useState<string | null>(null);
175
+ return (
176
+ <div>
177
+ <SavedCards
178
+ selectedToken={token}
179
+ onSelectCard={(card, cvc) => { /* ... */ }}
180
+ onAddNewCard={() => { /* show NuveiPaymentForm */ }}
181
+ />
182
+ <NuveiPaymentForm
183
+ uid={user.uid}
184
+ email={user.email}
185
+ onTokenSuccess={(token, cardInfo, saveCard) => { /* call /api/payment/charge */ }}
186
+ onTokenError={(err) => { /* ... */ }}
187
+ />
188
+ </div>
189
+ );
190
+ }
191
+ ```
192
+
193
+ The `NuveiPaymentForm` reads `NEXT_PUBLIC_NUVEI_CLIENT_APP_CODE`, `NEXT_PUBLIC_NUVEI_CLIENT_APP_KEY`, and `NEXT_PUBLIC_NUVEI_ENV` from your env by default. You can override per-instance with the `nuveiAppCode` / `nuveiAppKey` / `nuveiEnv` props.
194
+
195
+ ### Endpoints the UI components expect
196
+
197
+ The components POST/GET to fixed paths because the package's handler factories are designed to mount there:
198
+
199
+ | Component / call | Endpoint | Handler factory |
200
+ |---|---|---|
201
+ | `SavedCards` GET on mount | `/api/nuvei/cards` | `createCardsHandler` GET |
202
+ | `SavedCards` DELETE on remove | `/api/nuvei/cards` | `createCardsHandler` DELETE |
203
+ | `SavedCards` POST on verify | `/api/nuvei/verify` | `createVerifyHandler` POST |
204
+ | `NuveiPaymentForm` DELETE on duplicate | `/api/nuvei/cards` | `createCardsHandler` DELETE |
205
+ | `CardVisual` BIN lookup | `/api/bins` (overridable) | Consumer-provided JSON of `BinDatabase` |
206
+
207
+ The consumer must wire those routes — each `route.ts` is one or two lines re-exporting the factory result.
208
+
209
+ ## Migration from `pauhenriques-website`
210
+
211
+ See per-file `Source: pauhenriques-website/...` headers throughout the package. The refactor preserves behavior with three intentional fixes vs. the original (documented in commit `4b0cb06` — coupon-reuse in webhook BY_CRES + card-delete on 3DS flows).