1 | |
2 | type Paypal = { |
3 | clientId: string; |
4 | clientSecret: string; |
5 | }; |
6 |
|
7 | async function getToken(auth: Paypal): Promise<string> { |
8 | const url = new URL(`https://api-m.paypal.com/v1/oauth2/token`); |
9 | const response = await fetch(url, { |
10 | method: "POST", |
11 | headers: { |
12 | Authorization: `Basic ${btoa(`${auth.clientId}:${auth.clientSecret}`)}`, |
13 | }, |
14 | body: new URLSearchParams({ |
15 | grant_type: "client_credentials", |
16 | }), |
17 | }); |
18 | if (!response.ok) { |
19 | const text = await response.text(); |
20 | throw new Error(`Could not get token: ${response.status} ${text}`); |
21 | } |
22 | const json = await response.json(); |
23 | return json.access_token; |
24 | } |
25 | |
26 | * Confirm the Order |
27 | * Payer confirms their intent to pay for the the Order with the given payment source. |
28 | */ |
29 | export async function main( |
30 | auth: Paypal, |
31 | id: string, |
32 | PayPal_Client_Metadata_Id: string, |
33 | Prefer: string, |
34 | body: { |
35 | payment_source: { |
36 | card?: { |
37 | id?: string; |
38 | name?: string; |
39 | number?: string; |
40 | expiry?: string; |
41 | security_code?: string; |
42 | last_digits?: string; |
43 | card_type?: |
44 | | "VISA" |
45 | | "MASTERCARD" |
46 | | "DISCOVER" |
47 | | "AMEX" |
48 | | "SOLO" |
49 | | "JCB" |
50 | | "STAR" |
51 | | "DELTA" |
52 | | "SWITCH" |
53 | | "MAESTRO" |
54 | | "CB_NATIONALE" |
55 | | "CONFIGOGA" |
56 | | "CONFIDIS" |
57 | | "ELECTRON" |
58 | | "CETELEM" |
59 | | "CHINA_UNION_PAY"; |
60 | type?: "CREDIT" | "DEBIT" | "PREPAID" | "STORE" | "UNKNOWN"; |
61 | brand?: |
62 | | "VISA" |
63 | | "MASTERCARD" |
64 | | "DISCOVER" |
65 | | "AMEX" |
66 | | "SOLO" |
67 | | "JCB" |
68 | | "STAR" |
69 | | "DELTA" |
70 | | "SWITCH" |
71 | | "MAESTRO" |
72 | | "CB_NATIONALE" |
73 | | "CONFIGOGA" |
74 | | "CONFIDIS" |
75 | | "ELECTRON" |
76 | | "CETELEM" |
77 | | "CHINA_UNION_PAY"; |
78 | billing_address?: { |
79 | address_line_1?: string; |
80 | address_line_2?: string; |
81 | address_line_3?: string; |
82 | admin_area_4?: string; |
83 | admin_area_3?: string; |
84 | admin_area_2?: string; |
85 | admin_area_1?: string; |
86 | postal_code?: string; |
87 | country_code: string; |
88 | address_details?: { |
89 | street_number?: string; |
90 | street_name?: string; |
91 | street_type?: string; |
92 | delivery_service?: string; |
93 | building_name?: string; |
94 | sub_building?: string; |
95 | }; |
96 | }; |
97 | attributes?: { |
98 | customer?: { |
99 | id?: string; |
100 | email_address?: string; |
101 | phone?: { |
102 | phone_type?: "FAX" | "HOME" | "MOBILE" | "OTHER" | "PAGER"; |
103 | phone_number: { |
104 | country_code: string; |
105 | national_number: string; |
106 | extension_number?: string; |
107 | }; |
108 | }; |
109 | }; |
110 | vault?: { store_in_vault?: "ON_SUCCESS" }; |
111 | }; |
112 | } & { |
113 | vault_id?: string; |
114 | stored_credential?: { |
115 | payment_initiator: "CUSTOMER" | "MERCHANT"; |
116 | payment_type: "ONE_TIME" | "RECURRING" | "UNSCHEDULED"; |
117 | usage?: "FIRST" | "SUBSEQUENT" | "DERIVED"; |
118 | previous_network_transaction_reference?: { |
119 | id: string; |
120 | date?: string; |
121 | network?: |
122 | | "VISA" |
123 | | "MASTERCARD" |
124 | | "DISCOVER" |
125 | | "AMEX" |
126 | | "SOLO" |
127 | | "JCB" |
128 | | "STAR" |
129 | | "DELTA" |
130 | | "SWITCH" |
131 | | "MAESTRO" |
132 | | "CB_NATIONALE" |
133 | | "CONFIGOGA" |
134 | | "CONFIDIS" |
135 | | "ELECTRON" |
136 | | "CETELEM" |
137 | | "CHINA_UNION_PAY"; |
138 | acquirer_reference_number?: string; |
139 | }; |
140 | }; |
141 | network_token?: { |
142 | number: string; |
143 | expiry: string; |
144 | cryptogram?: string; |
145 | eci_flag?: |
146 | | "MASTERCARD_NON_3D_SECURE_TRANSACTION" |
147 | | "MASTERCARD_ATTEMPTED_AUTHENTICATION_TRANSACTION" |
148 | | "MASTERCARD_FULLY_AUTHENTICATED_TRANSACTION" |
149 | | "FULLY_AUTHENTICATED_TRANSACTION" |
150 | | "ATTEMPTED_AUTHENTICATION_TRANSACTION" |
151 | | "NON_3D_SECURE_TRANSACTION"; |
152 | token_requestor_id?: string; |
153 | }; |
154 | experience_context?: { return_url?: string; cancel_url?: string }; |
155 | }; |
156 | token?: { id: string; type: "BILLING_AGREEMENT" }; |
157 | paypal?: { |
158 | vault_id?: string; |
159 | email_address?: string; |
160 | name?: { |
161 | prefix?: string; |
162 | given_name?: string; |
163 | surname?: string; |
164 | middle_name?: string; |
165 | suffix?: string; |
166 | full_name?: string; |
167 | }; |
168 | phone?: { |
169 | phone_type?: "FAX" | "HOME" | "MOBILE" | "OTHER" | "PAGER"; |
170 | phone_number: { |
171 | country_code: string; |
172 | national_number: string; |
173 | extension_number?: string; |
174 | }; |
175 | }; |
176 | birth_date?: string; |
177 | tax_info?: { tax_id: string; tax_id_type: "BR_CPF" | "BR_CNPJ" }; |
178 | address?: { |
179 | address_line_1?: string; |
180 | address_line_2?: string; |
181 | address_line_3?: string; |
182 | admin_area_4?: string; |
183 | admin_area_3?: string; |
184 | admin_area_2?: string; |
185 | admin_area_1?: string; |
186 | postal_code?: string; |
187 | country_code: string; |
188 | address_details?: { |
189 | street_number?: string; |
190 | street_name?: string; |
191 | street_type?: string; |
192 | delivery_service?: string; |
193 | building_name?: string; |
194 | sub_building?: string; |
195 | }; |
196 | }; |
197 | attributes?: { |
198 | customer?: { |
199 | id?: string; |
200 | email_address?: string; |
201 | phone?: { |
202 | phone_type?: "FAX" | "HOME" | "MOBILE" | "OTHER" | "PAGER"; |
203 | phone_number: { |
204 | country_code: string; |
205 | national_number: string; |
206 | extension_number?: string; |
207 | }; |
208 | }; |
209 | } & {}; |
210 | vault?: { store_in_vault?: "ON_SUCCESS" } & { |
211 | description?: string; |
212 | usage_pattern?: |
213 | | "IMMEDIATE" |
214 | | "DEFERRED" |
215 | | "RECURRING_PREPAID" |
216 | | "RECURRING_POSTPAID" |
217 | | "THRESHOLD_PREPAID" |
218 | | "THRESHOLD_POSTPAID"; |
219 | shipping?: { |
220 | name?: { |
221 | prefix?: string; |
222 | given_name?: string; |
223 | surname?: string; |
224 | middle_name?: string; |
225 | suffix?: string; |
226 | alternate_full_name?: string; |
227 | full_name?: string; |
228 | }; |
229 | type?: |
230 | | "SHIPPING" |
231 | | "PICKUP_IN_PERSON" |
232 | | "PICKUP_IN_STORE" |
233 | | "PICKUP_FROM_PERSON"; |
234 | options?: { |
235 | id: string; |
236 | label: string; |
237 | type?: |
238 | | "SHIPPING" |
239 | | "PICKUP_IN_STORE" |
240 | | "PICKUP_FROM_PERSON" |
241 | | "PICKUP"; |
242 | amount?: { currency_code: string; value: string }; |
243 | selected: false | true; |
244 | }[]; |
245 | address?: { |
246 | address_line_1?: string; |
247 | address_line_2?: string; |
248 | address_line_3?: string; |
249 | admin_area_4?: string; |
250 | admin_area_3?: string; |
251 | admin_area_2?: string; |
252 | admin_area_1?: string; |
253 | postal_code?: string; |
254 | country_code: string; |
255 | address_details?: { |
256 | street_number?: string; |
257 | street_name?: string; |
258 | street_type?: string; |
259 | delivery_service?: string; |
260 | building_name?: string; |
261 | sub_building?: string; |
262 | }; |
263 | }; |
264 | }; |
265 | usage_type?: "MERCHANT" | "PLATFORM"; |
266 | owner_id?: unknown; |
267 | customer_type?: "CONSUMER" | "BUSINESS"; |
268 | permit_multiple_payment_tokens?: false | true; |
269 | }; |
270 | }; |
271 | experience_context?: { |
272 | brand_name?: string; |
273 | locale?: string; |
274 | shipping_preference?: |
275 | | "GET_FROM_FILE" |
276 | | "NO_SHIPPING" |
277 | | "SET_PROVIDED_ADDRESS"; |
278 | return_url?: string; |
279 | cancel_url?: string; |
280 | landing_page?: "LOGIN" | "GUEST_CHECKOUT" | "NO_PREFERENCE"; |
281 | user_action?: "CONTINUE" | "PAY_NOW"; |
282 | payment_method_preference?: |
283 | | "UNRESTRICTED" |
284 | | "IMMEDIATE_PAYMENT_REQUIRED"; |
285 | }; |
286 | billing_agreement_id?: string; |
287 | }; |
288 | bancontact?: { |
289 | name: string; |
290 | country_code: string; |
291 | experience_context?: { |
292 | brand_name?: string; |
293 | locale?: string; |
294 | shipping_preference?: |
295 | | "GET_FROM_FILE" |
296 | | "NO_SHIPPING" |
297 | | "SET_PROVIDED_ADDRESS"; |
298 | return_url?: string; |
299 | cancel_url?: string; |
300 | }; |
301 | attributes?: {}; |
302 | }; |
303 | blik?: { |
304 | name: string; |
305 | country_code: string; |
306 | email?: string; |
307 | experience_context?: { |
308 | brand_name?: string; |
309 | locale?: string; |
310 | shipping_preference?: |
311 | | "GET_FROM_FILE" |
312 | | "NO_SHIPPING" |
313 | | "SET_PROVIDED_ADDRESS"; |
314 | return_url?: string; |
315 | cancel_url?: string; |
316 | } & { consumer_ip?: string; consumer_user_agent?: string }; |
317 | level_0?: { auth_code: string }; |
318 | one_click?: { |
319 | auth_code?: string; |
320 | consumer_reference: string; |
321 | alias_label?: string; |
322 | alias_key?: string; |
323 | }; |
324 | }; |
325 | eps?: { |
326 | name: string; |
327 | country_code: string; |
328 | experience_context?: { |
329 | brand_name?: string; |
330 | locale?: string; |
331 | shipping_preference?: |
332 | | "GET_FROM_FILE" |
333 | | "NO_SHIPPING" |
334 | | "SET_PROVIDED_ADDRESS"; |
335 | return_url?: string; |
336 | cancel_url?: string; |
337 | }; |
338 | }; |
339 | giropay?: { |
340 | name: string; |
341 | country_code: string; |
342 | experience_context?: { |
343 | brand_name?: string; |
344 | locale?: string; |
345 | shipping_preference?: |
346 | | "GET_FROM_FILE" |
347 | | "NO_SHIPPING" |
348 | | "SET_PROVIDED_ADDRESS"; |
349 | return_url?: string; |
350 | cancel_url?: string; |
351 | }; |
352 | }; |
353 | ideal?: { |
354 | name: string; |
355 | country_code: string; |
356 | bic?: string; |
357 | experience_context?: { |
358 | brand_name?: string; |
359 | locale?: string; |
360 | shipping_preference?: |
361 | | "GET_FROM_FILE" |
362 | | "NO_SHIPPING" |
363 | | "SET_PROVIDED_ADDRESS"; |
364 | return_url?: string; |
365 | cancel_url?: string; |
366 | }; |
367 | attributes?: {}; |
368 | }; |
369 | mybank?: { |
370 | name: string; |
371 | country_code: string; |
372 | experience_context?: { |
373 | brand_name?: string; |
374 | locale?: string; |
375 | shipping_preference?: |
376 | | "GET_FROM_FILE" |
377 | | "NO_SHIPPING" |
378 | | "SET_PROVIDED_ADDRESS"; |
379 | return_url?: string; |
380 | cancel_url?: string; |
381 | }; |
382 | }; |
383 | p24?: { |
384 | name: string; |
385 | email: string; |
386 | country_code: string; |
387 | experience_context?: { |
388 | brand_name?: string; |
389 | locale?: string; |
390 | shipping_preference?: |
391 | | "GET_FROM_FILE" |
392 | | "NO_SHIPPING" |
393 | | "SET_PROVIDED_ADDRESS"; |
394 | return_url?: string; |
395 | cancel_url?: string; |
396 | }; |
397 | }; |
398 | sofort?: { |
399 | name: string; |
400 | country_code: string; |
401 | experience_context?: { |
402 | brand_name?: string; |
403 | locale?: string; |
404 | shipping_preference?: |
405 | | "GET_FROM_FILE" |
406 | | "NO_SHIPPING" |
407 | | "SET_PROVIDED_ADDRESS"; |
408 | return_url?: string; |
409 | cancel_url?: string; |
410 | }; |
411 | }; |
412 | trustly?: { |
413 | name: string; |
414 | country_code: string; |
415 | experience_context?: { |
416 | brand_name?: string; |
417 | locale?: string; |
418 | shipping_preference?: |
419 | | "GET_FROM_FILE" |
420 | | "NO_SHIPPING" |
421 | | "SET_PROVIDED_ADDRESS"; |
422 | return_url?: string; |
423 | cancel_url?: string; |
424 | }; |
425 | }; |
426 | apple_pay?: { |
427 | id?: string; |
428 | name?: string; |
429 | email_address?: string; |
430 | phone_number?: { |
431 | country_code: string; |
432 | national_number: string; |
433 | extension_number?: string; |
434 | }; |
435 | decrypted_token?: { |
436 | transaction_amount?: { currency_code: string; value: string }; |
437 | tokenized_card: { |
438 | id?: string; |
439 | name?: string; |
440 | number?: string; |
441 | expiry?: string; |
442 | security_code?: string; |
443 | last_digits?: string; |
444 | card_type?: |
445 | | "VISA" |
446 | | "MASTERCARD" |
447 | | "DISCOVER" |
448 | | "AMEX" |
449 | | "SOLO" |
450 | | "JCB" |
451 | | "STAR" |
452 | | "DELTA" |
453 | | "SWITCH" |
454 | | "MAESTRO" |
455 | | "CB_NATIONALE" |
456 | | "CONFIGOGA" |
457 | | "CONFIDIS" |
458 | | "ELECTRON" |
459 | | "CETELEM" |
460 | | "CHINA_UNION_PAY"; |
461 | type?: "CREDIT" | "DEBIT" | "PREPAID" | "STORE" | "UNKNOWN"; |
462 | brand?: |
463 | | "VISA" |
464 | | "MASTERCARD" |
465 | | "DISCOVER" |
466 | | "AMEX" |
467 | | "SOLO" |
468 | | "JCB" |
469 | | "STAR" |
470 | | "DELTA" |
471 | | "SWITCH" |
472 | | "MAESTRO" |
473 | | "CB_NATIONALE" |
474 | | "CONFIGOGA" |
475 | | "CONFIDIS" |
476 | | "ELECTRON" |
477 | | "CETELEM" |
478 | | "CHINA_UNION_PAY"; |
479 | billing_address?: { |
480 | address_line_1?: string; |
481 | address_line_2?: string; |
482 | address_line_3?: string; |
483 | admin_area_4?: string; |
484 | admin_area_3?: string; |
485 | admin_area_2?: string; |
486 | admin_area_1?: string; |
487 | postal_code?: string; |
488 | country_code: string; |
489 | address_details?: { |
490 | street_number?: string; |
491 | street_name?: string; |
492 | street_type?: string; |
493 | delivery_service?: string; |
494 | building_name?: string; |
495 | sub_building?: string; |
496 | }; |
497 | }; |
498 | attributes?: { |
499 | customer?: { |
500 | id?: string; |
501 | email_address?: string; |
502 | phone?: { |
503 | phone_type?: "FAX" | "HOME" | "MOBILE" | "OTHER" | "PAGER"; |
504 | phone_number: { |
505 | country_code: string; |
506 | national_number: string; |
507 | extension_number?: string; |
508 | }; |
509 | }; |
510 | }; |
511 | vault?: { store_in_vault?: "ON_SUCCESS" }; |
512 | }; |
513 | }; |
514 | device_manufacturer_id?: string; |
515 | payment_data_type?: "3DSECURE" | "EMV"; |
516 | payment_data?: { |
517 | cryptogram?: string; |
518 | eci_indicator?: string; |
519 | emv_data?: string; |
520 | pin?: string; |
521 | }; |
522 | }; |
523 | stored_credential?: { |
524 | payment_initiator: "CUSTOMER" | "MERCHANT"; |
525 | payment_type: "ONE_TIME" | "RECURRING" | "UNSCHEDULED"; |
526 | usage?: "FIRST" | "SUBSEQUENT" | "DERIVED"; |
527 | previous_network_transaction_reference?: { |
528 | id: string; |
529 | date?: string; |
530 | network?: |
531 | | "VISA" |
532 | | "MASTERCARD" |
533 | | "DISCOVER" |
534 | | "AMEX" |
535 | | "SOLO" |
536 | | "JCB" |
537 | | "STAR" |
538 | | "DELTA" |
539 | | "SWITCH" |
540 | | "MAESTRO" |
541 | | "CB_NATIONALE" |
542 | | "CONFIGOGA" |
543 | | "CONFIDIS" |
544 | | "ELECTRON" |
545 | | "CETELEM" |
546 | | "CHINA_UNION_PAY"; |
547 | acquirer_reference_number?: string; |
548 | }; |
549 | }; |
550 | vault_id?: string; |
551 | attributes?: unknown; |
552 | }; |
553 | google_pay?: {}; |
554 | venmo?: { |
555 | vault_id?: string; |
556 | email_address?: string; |
557 | experience_context?: { |
558 | brand_name?: string; |
559 | shipping_preference?: |
560 | | "GET_FROM_FILE" |
561 | | "NO_SHIPPING" |
562 | | "SET_PROVIDED_ADDRESS"; |
563 | }; |
564 | attributes?: { |
565 | customer?: { |
566 | id?: string; |
567 | email_address?: string; |
568 | phone?: { |
569 | phone_type?: "FAX" | "HOME" | "MOBILE" | "OTHER" | "PAGER"; |
570 | phone_number: { |
571 | country_code: string; |
572 | national_number: string; |
573 | extension_number?: string; |
574 | }; |
575 | }; |
576 | }; |
577 | vault?: { store_in_vault: "ON_SUCCESS" } & { |
578 | description?: string; |
579 | usage_pattern?: |
580 | | "IMMEDIATE" |
581 | | "DEFERRED" |
582 | | "RECURRING_PREPAID" |
583 | | "RECURRING_POSTPAID" |
584 | | "THRESHOLD_PREPAID" |
585 | | "THRESHOLD_POSTPAID"; |
586 | usage_type?: "MERCHANT" | "PLATFORM"; |
587 | customer_type?: "CONSUMER" | "BUSINESS"; |
588 | permit_multiple_payment_tokens?: false | true; |
589 | }; |
590 | }; |
591 | }; |
592 | }; |
593 | processing_instruction?: |
594 | | "ORDER_COMPLETE_ON_PAYMENT_APPROVAL" |
595 | | "NO_INSTRUCTION"; |
596 | application_context?: { |
597 | brand_name?: string; |
598 | locale?: string; |
599 | return_url?: string; |
600 | cancel_url?: string; |
601 | stored_payment_source?: { |
602 | payment_initiator: "CUSTOMER" | "MERCHANT"; |
603 | payment_type: "ONE_TIME" | "RECURRING" | "UNSCHEDULED"; |
604 | usage?: "FIRST" | "SUBSEQUENT" | "DERIVED"; |
605 | previous_network_transaction_reference?: { |
606 | id: string; |
607 | date?: string; |
608 | network?: |
609 | | "VISA" |
610 | | "MASTERCARD" |
611 | | "DISCOVER" |
612 | | "AMEX" |
613 | | "SOLO" |
614 | | "JCB" |
615 | | "STAR" |
616 | | "DELTA" |
617 | | "SWITCH" |
618 | | "MAESTRO" |
619 | | "CB_NATIONALE" |
620 | | "CONFIGOGA" |
621 | | "CONFIDIS" |
622 | | "ELECTRON" |
623 | | "CETELEM" |
624 | | "CHINA_UNION_PAY"; |
625 | acquirer_reference_number?: string; |
626 | }; |
627 | }; |
628 | }; |
629 | }, |
630 | ) { |
631 | const token = await getToken(auth); |
632 | const url = new URL( |
633 | `https://api-m.paypal.com/v2/checkout/orders/${id}/confirm-payment-source`, |
634 | ); |
635 |
|
636 | const response = await fetch(url, { |
637 | method: "POST", |
638 | headers: { |
639 | "PayPal-Client-Metadata-Id": PayPal_Client_Metadata_Id, |
640 | Prefer: Prefer, |
641 | "Content-Type": "application/json", |
642 | Authorization: "Bearer " + token, |
643 | }, |
644 | body: JSON.stringify(body), |
645 | }); |
646 | if (!response.ok) { |
647 | const text = await response.text(); |
648 | throw new Error(`${response.status} ${text}`); |
649 | } |
650 | return await response.json(); |
651 | } |
652 |
|