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 | * Create draft invoice |
27 | * Creates a draft invoice. To move the invoice from a draft to payable state, you must send the invoice.In the JSON request body, include invoice details including merchant information. The invoice object must include an items array.Note: The merchant that you specify in an invoice must have a PayPal account in good standing.. |
28 | */ |
29 | export async function main( |
30 | auth: Paypal, |
31 | body: { |
32 | id?: string; |
33 | parent_id?: string; |
34 | status?: |
35 | | "DRAFT" |
36 | | "SENT" |
37 | | "SCHEDULED" |
38 | | "PAID" |
39 | | "MARKED_AS_PAID" |
40 | | "CANCELLED" |
41 | | "REFUNDED" |
42 | | "PARTIALLY_PAID" |
43 | | "PARTIALLY_REFUNDED" |
44 | | "MARKED_AS_REFUNDED" |
45 | | "UNPAID" |
46 | | "PAYMENT_PENDING"; |
47 | detail: { |
48 | reference?: string; |
49 | currency_code: string; |
50 | note?: string; |
51 | terms_and_conditions?: string; |
52 | memo?: string; |
53 | attachments?: { |
54 | id?: string; |
55 | reference_url?: string; |
56 | content_type?: string; |
57 | create_time?: string; |
58 | size?: string; |
59 | }[]; |
60 | } & { |
61 | invoice_number?: string; |
62 | invoice_date?: string; |
63 | payment_term?: { |
64 | term_type?: |
65 | | "DUE_ON_RECEIPT" |
66 | | "DUE_ON_DATE_SPECIFIED" |
67 | | "NET_10" |
68 | | "NET_15" |
69 | | "NET_30" |
70 | | "NET_45" |
71 | | "NET_60" |
72 | | "NET_90" |
73 | | "NO_DUE_DATE"; |
74 | } & { due_date?: string }; |
75 | metadata?: { |
76 | create_time?: string; |
77 | created_by?: string; |
78 | last_update_time?: string; |
79 | last_updated_by?: string; |
80 | } & { |
81 | cancel_time?: string; |
82 | cancelled_by?: string; |
83 | first_sent_time?: string; |
84 | last_sent_time?: string; |
85 | last_sent_by?: string; |
86 | created_by_flow?: |
87 | | "MULTIPLE_RECIPIENTS_GROUP" |
88 | | "BATCH" |
89 | | "REGULAR_SINGLE"; |
90 | recipient_view_url?: string; |
91 | invoicer_view_url?: string; |
92 | }; |
93 | }; |
94 | invoicer?: { business_name?: string } & { |
95 | name?: { |
96 | prefix?: string; |
97 | given_name?: string; |
98 | surname?: string; |
99 | middle_name?: string; |
100 | suffix?: string; |
101 | alternate_full_name?: string; |
102 | full_name?: string; |
103 | }; |
104 | address?: { |
105 | address_line_1?: string; |
106 | address_line_2?: string; |
107 | address_line_3?: string; |
108 | admin_area_4?: string; |
109 | admin_area_3?: string; |
110 | admin_area_2?: string; |
111 | admin_area_1?: string; |
112 | postal_code?: string; |
113 | country_code: string; |
114 | address_details?: { |
115 | street_number?: string; |
116 | street_name?: string; |
117 | street_type?: string; |
118 | delivery_service?: string; |
119 | building_name?: string; |
120 | sub_building?: string; |
121 | }; |
122 | }; |
123 | } & { |
124 | email_address?: string; |
125 | phones?: { |
126 | country_code: string; |
127 | national_number: string; |
128 | extension_number?: string; |
129 | } & { phone_type?: "FAX" | "HOME" | "MOBILE" | "OTHER" | "PAGER" }[]; |
130 | website?: string; |
131 | tax_id?: string; |
132 | additional_notes?: string; |
133 | logo_url?: string; |
134 | }; |
135 | primary_recipients?: { |
136 | billing_info?: { business_name?: string } & { |
137 | name?: { |
138 | prefix?: string; |
139 | given_name?: string; |
140 | surname?: string; |
141 | middle_name?: string; |
142 | suffix?: string; |
143 | alternate_full_name?: string; |
144 | full_name?: string; |
145 | }; |
146 | address?: { |
147 | address_line_1?: string; |
148 | address_line_2?: string; |
149 | address_line_3?: string; |
150 | admin_area_4?: string; |
151 | admin_area_3?: string; |
152 | admin_area_2?: string; |
153 | admin_area_1?: string; |
154 | postal_code?: string; |
155 | country_code: string; |
156 | address_details?: { |
157 | street_number?: string; |
158 | street_name?: string; |
159 | street_type?: string; |
160 | delivery_service?: string; |
161 | building_name?: string; |
162 | sub_building?: string; |
163 | }; |
164 | }; |
165 | } & { |
166 | email_address?: string; |
167 | phones?: { |
168 | country_code: string; |
169 | national_number: string; |
170 | extension_number?: string; |
171 | } & { phone_type?: "FAX" | "HOME" | "MOBILE" | "OTHER" | "PAGER" }[]; |
172 | additional_info?: string; |
173 | language?: string; |
174 | }; |
175 | shipping_info?: { business_name?: string } & { |
176 | name?: { |
177 | prefix?: string; |
178 | given_name?: string; |
179 | surname?: string; |
180 | middle_name?: string; |
181 | suffix?: string; |
182 | alternate_full_name?: string; |
183 | full_name?: string; |
184 | }; |
185 | address?: { |
186 | address_line_1?: string; |
187 | address_line_2?: string; |
188 | address_line_3?: string; |
189 | admin_area_4?: string; |
190 | admin_area_3?: string; |
191 | admin_area_2?: string; |
192 | admin_area_1?: string; |
193 | postal_code?: string; |
194 | country_code: string; |
195 | address_details?: { |
196 | street_number?: string; |
197 | street_name?: string; |
198 | street_type?: string; |
199 | delivery_service?: string; |
200 | building_name?: string; |
201 | sub_building?: string; |
202 | }; |
203 | }; |
204 | }; |
205 | }[]; |
206 | additional_recipients?: string[]; |
207 | items?: { |
208 | id?: string; |
209 | name: string; |
210 | description?: string; |
211 | quantity: string; |
212 | unit_amount: { currency_code: string; value: string }; |
213 | tax?: { |
214 | name: string; |
215 | percent: string; |
216 | amount?: { currency_code: string; value: string }; |
217 | }; |
218 | item_date?: string; |
219 | discount?: { |
220 | percent?: string; |
221 | amount?: { currency_code: string; value: string }; |
222 | }; |
223 | unit_of_measure?: "QUANTITY" | "HOURS" | "AMOUNT"; |
224 | }[]; |
225 | configuration?: { |
226 | tax_calculated_after_discount?: false | true; |
227 | tax_inclusive?: false | true; |
228 | allow_tip?: false | true; |
229 | partial_payment?: { |
230 | allow_partial_payment?: false | true; |
231 | minimum_amount_due?: { currency_code: string; value: string }; |
232 | }; |
233 | } & { template_id?: string }; |
234 | amount?: { |
235 | currency_code?: string; |
236 | value?: string; |
237 | breakdown?: { |
238 | item_total?: { currency_code: string; value: string }; |
239 | discount?: { |
240 | invoice_discount?: { |
241 | percent?: string; |
242 | amount?: { currency_code: string; value: string }; |
243 | }; |
244 | item_discount?: { currency_code: string; value: string }; |
245 | }; |
246 | tax_total?: { currency_code: string; value: string }; |
247 | shipping?: { |
248 | amount?: { currency_code: string; value: string }; |
249 | tax?: { |
250 | name: string; |
251 | percent: string; |
252 | amount?: { currency_code: string; value: string }; |
253 | }; |
254 | }; |
255 | custom?: { |
256 | label: string; |
257 | amount?: { currency_code: string; value: string }; |
258 | }; |
259 | }; |
260 | }; |
261 | due_amount?: { currency_code: string; value: string }; |
262 | gratuity?: { currency_code: string; value: string }; |
263 | payments?: { |
264 | paid_amount?: { currency_code: string; value: string }; |
265 | transactions?: { |
266 | type?: "PAYPAL" | "EXTERNAL"; |
267 | payment_id?: string; |
268 | payment_date?: string; |
269 | method: |
270 | | "OTHER" |
271 | | "PAYPAL" |
272 | | "BANK_TRANSFER" |
273 | | "CASH" |
274 | | "CHECK" |
275 | | "CREDIT_CARD" |
276 | | "DEBIT_CARD" |
277 | | "WIRE_TRANSFER"; |
278 | note?: string; |
279 | amount?: { currency_code: string; value: string }; |
280 | shipping_info?: { business_name?: string } & { |
281 | name?: { |
282 | prefix?: string; |
283 | given_name?: string; |
284 | surname?: string; |
285 | middle_name?: string; |
286 | suffix?: string; |
287 | alternate_full_name?: string; |
288 | full_name?: string; |
289 | }; |
290 | address?: { |
291 | address_line_1?: string; |
292 | address_line_2?: string; |
293 | address_line_3?: string; |
294 | admin_area_4?: string; |
295 | admin_area_3?: string; |
296 | admin_area_2?: string; |
297 | admin_area_1?: string; |
298 | postal_code?: string; |
299 | country_code: string; |
300 | address_details?: { |
301 | street_number?: string; |
302 | street_name?: string; |
303 | street_type?: string; |
304 | delivery_service?: string; |
305 | building_name?: string; |
306 | sub_building?: string; |
307 | }; |
308 | }; |
309 | }; |
310 | }[]; |
311 | }; |
312 | refunds?: { |
313 | refund_amount?: { currency_code: string; value: string }; |
314 | transactions?: { |
315 | type?: "PAYPAL" | "EXTERNAL"; |
316 | refund_id?: string; |
317 | refund_date?: string; |
318 | amount?: { currency_code: string; value: string }; |
319 | method: |
320 | | "OTHER" |
321 | | "PAYPAL" |
322 | | "BANK_TRANSFER" |
323 | | "CASH" |
324 | | "CHECK" |
325 | | "CREDIT_CARD" |
326 | | "DEBIT_CARD" |
327 | | "WIRE_TRANSFER"; |
328 | }[]; |
329 | }; |
330 | links?: { |
331 | href: string; |
332 | rel: string; |
333 | method?: |
334 | | "GET" |
335 | | "POST" |
336 | | "PUT" |
337 | | "DELETE" |
338 | | "HEAD" |
339 | | "CONNECT" |
340 | | "OPTIONS" |
341 | | "PATCH"; |
342 | }[]; |
343 | }, |
344 | ) { |
345 | const token = await getToken(auth); |
346 | const url = new URL(`https://api-m.paypal.com/v2/invoicing/invoices`); |
347 |
|
348 | const response = await fetch(url, { |
349 | method: "POST", |
350 | headers: { |
351 | "Content-Type": "application/json", |
352 | Authorization: "Bearer " + token, |
353 | }, |
354 | body: JSON.stringify(body), |
355 | }); |
356 | if (!response.ok) { |
357 | const text = await response.text(); |
358 | throw new Error(`${response.status} ${text}`); |
359 | } |
360 | return await response.json(); |
361 | } |
362 |
|