clink-typescript-sdk-test 0.0.3

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,189 @@
1
+ # ClinkPay Client SDK Guide
2
+
3
+ Lightweight client for Clink billing APIs, It supports `production` and `sandbox` environments and handles authentication and errors for you.
4
+
5
+ ## Requirements
6
+ - Runtime with `fetch` support (e.g., Node.js 18+).
7
+ - Only `env` values `production` and `sandbox` are supported; any other value throws. the default value is `production`.
8
+
9
+ ## Import
10
+ Choose your package management tool (such as npm, yarn, or pnpm) and import it according to your project configuration.
11
+
12
+ ```sh
13
+ npm install clink-typescript-sdk
14
+ ```
15
+
16
+ ## Quick Start
17
+ ```ts
18
+ import { ClinkPayClient } from 'clink-typescript-sdk';
19
+
20
+ const client = new ClinkPayClient({
21
+ apiKey: 'YOUR_API_KEY',
22
+ env: 'sandbox',
23
+ });
24
+
25
+ async function main() {
26
+ const session = await client.createCheckoutSession({
27
+ originalAmount: 1999,
28
+ originalCurrency: 'USD',
29
+ successUrl: 'https://merchant.example.com/success',
30
+ cancelUrl: 'https://merchant.example.com/cancel',
31
+ allowPromotionCodes: true,
32
+ });
33
+
34
+ console.log(session.sessionId);
35
+ console.log(session.url);
36
+ }
37
+
38
+ main();
39
+ ```
40
+
41
+ ## API Overview
42
+ - `createCheckoutSession(options)`: Create a checkout session and get the redirect `url`.
43
+ - `getCheckoutSession(sessionId)`: Retrieve checkout session details.
44
+ - `getOrder(orderId)`: Retrieve order details.
45
+ - `getRefund(refundId)`: Retrieve refund details.
46
+ - `getSubscription(subscriptionId)`: Retrieve subscription details.
47
+ - `getInvoice(invoiceId)`: Retrieve subscription invoice details.
48
+ - `customerPortalSession(options)`: Create a customer portal session and get the access link.
49
+
50
+ All methods are asynchronous and throw on errors.
51
+
52
+ ## Error Handling
53
+ On non-successful responses or network errors, methods throw exceptions:
54
+ - Business errors throw `ClinkApiError`.
55
+ - Non-business errors throw standard `Error`.
56
+
57
+ Example:
58
+ ```ts
59
+ import { ClinkPayClient } from 'clink-typescript-sdk';
60
+
61
+ const client = new ClinkPayClient({ apiKey: 'YOUR_API_KEY', env: 'sandbox' });
62
+
63
+ async function demo() {
64
+ try {
65
+ const order = await client.getOrder('ord_123');
66
+ console.log(order.status);
67
+ } catch (e) {
68
+ if (e instanceof ClinkApiError) {
69
+ const { code, message } = e;
70
+ // your code here
71
+ }
72
+ // handle other errors
73
+ }
74
+ }
75
+
76
+ demo();
77
+ ```
78
+
79
+ ## Detailed Examples
80
+
81
+ ### Create a Checkout Session
82
+ ```ts
83
+ const client = new ClinkPayClient({ apiKey: 'YOUR_API_KEY', env: 'sandbox' });
84
+ const session = await client.createCheckoutSession({
85
+ originalAmount: 4999,
86
+ originalCurrency: 'USD',
87
+ successUrl: 'https://merchant.example.com/success',
88
+ cancelUrl: 'https://merchant.example.com/cancel',
89
+ });
90
+ console.log(session.sessionId);
91
+ console.log(session.url);
92
+ ```
93
+
94
+ ### Get Checkout Session
95
+ ```ts
96
+ const info = await client.getCheckoutSession('sess_123');
97
+ console.log(info.status);
98
+ ```
99
+
100
+ ### Get Order
101
+ ```ts
102
+ const order = await client.getOrder('ord_123');
103
+ console.log(order.amountTotal);
104
+ ```
105
+
106
+
107
+ ### Get Refund
108
+ ```ts
109
+ const refund = await client.getRefund('rfd_123');
110
+ console.log(refund.status);
111
+ ```
112
+
113
+ ### Get Subscription
114
+ ```ts
115
+ const sub = await client.getSubscription('sub_123');
116
+ console.log(sub.status);
117
+ ```
118
+
119
+ ### Get Invoice
120
+ ```ts
121
+ const invoice = await client.getInvoice('inv_123');
122
+ console.log(invoice.status);
123
+ ```
124
+
125
+ ### Customer Portal Session
126
+ ```ts
127
+ const portal = await client.customerPortalSession({
128
+ customerId: 'cus_123',
129
+ returnUrl: 'https://merchant.example.com/return',
130
+ });
131
+ console.log(portal.url);
132
+ ```
133
+
134
+ ### Get Product Info
135
+
136
+ ```ts
137
+ const product = await client.getProduct('prod_123');
138
+ console.log(product);
139
+ ```
140
+
141
+ ### Get Product List
142
+ ```ts
143
+ const products = await client.getProductList({
144
+ page: 1,
145
+ pageSize: 10,
146
+ });
147
+ console.log(products);
148
+ ```
149
+
150
+ ### Get Price Info
151
+ ```ts
152
+ const price = await client.getPrice('price_123');
153
+ console.log(price);
154
+ ```
155
+ ### Get Price List
156
+ ```ts
157
+ const prices = await client.getPriceList({
158
+ page: 1,
159
+ pageSize: 10,
160
+ });
161
+ console.log(prices);
162
+ ```
163
+
164
+ ### Get Payment Instrument List
165
+ ```ts
166
+ const paymentInstruments = await client.getPaymentInstrumentList({
167
+ customerId: 'cus_123',
168
+ type: 'card',
169
+ });
170
+ console.log(paymentInstruments);
171
+ ```
172
+
173
+
174
+
175
+ ## webhook
176
+
177
+ ### Verify and Get Webhook Event
178
+ ```ts
179
+ import { ClinkWebhook } from 'clink-typescript-sdk';
180
+
181
+ const clinkWebhook = new ClinkWebhook({ signatureKey: 'YOUR_SIGNATURE_KEY' });
182
+ // if verify error, the verifyAndGet method will throw error
183
+ const event = clinkWebhook.verifyAndGet({
184
+ timestamp: 'timestamp from request header',
185
+ body: 'body(jsonString) from request body',
186
+ headerSignature: 'signature from request header',
187
+ });
188
+ console.log(event);
189
+ ```
@@ -0,0 +1,89 @@
1
+ import { ClinkPayClientConstructorOptions, CreateCheckoutSessionRequest, CreateCheckoutSessionResponse, CustomerPortalSessionRequest, CustomerPortalSessionResponse, GetInvoiceResponse, GetOrderResponse, GetPriceRequest, GetPriceResponse, GetProductResponse, GetSessionResponse, GetSubscriptionResponse, ListResult, PageQuery, PaymentInstrumentRequest, PaymentInstrumentResponse, RefundResponse } from './model';
2
+ export declare class ClinkPayClient {
3
+ private apiKey;
4
+ private baseUrl;
5
+ private requestClient;
6
+ constructor(options: ClinkPayClientConstructorOptions);
7
+ /**
8
+ * create checkout session
9
+ *
10
+ * @param options CreateCheckoutSessionRequest
11
+ * @returns sessionResponse
12
+ */
13
+ createCheckoutSession(options: CreateCheckoutSessionRequest): Promise<CreateCheckoutSessionResponse>;
14
+ /**
15
+ * get checkout session
16
+ *
17
+ * @param sessionId checkout session id
18
+ * @returns checkout session response
19
+ */
20
+ getCheckoutSession(sessionId: string): Promise<GetSessionResponse.SessionInfo>;
21
+ /**
22
+ * get order info
23
+ *
24
+ * @param orderId order id
25
+ * @returns order info response
26
+ */
27
+ getOrder(orderId: string): Promise<GetOrderResponse>;
28
+ /**
29
+ * get refund info
30
+ *
31
+ * @param refundId refund id
32
+ * @returns refund info response
33
+ */
34
+ getRefund(refundId: string): Promise<RefundResponse>;
35
+ /**
36
+ * get subscription info
37
+ *
38
+ * @param subscriptionId subscription id
39
+ * @returns subscription info response
40
+ */
41
+ getSubscription(subscriptionId: string): Promise<GetSubscriptionResponse.Data>;
42
+ /**
43
+ * get invoice info
44
+ *
45
+ * @param invoiceId invoice id
46
+ * @returns invoice info response
47
+ */
48
+ getInvoice(invoiceId: string): Promise<GetInvoiceResponse.Data>;
49
+ /**
50
+ * create customer portal session
51
+ *
52
+ * @param options CustomerPortalSessionRequest
53
+ * @returns customer portal session response
54
+ */
55
+ customerPortalSession(options: CustomerPortalSessionRequest): Promise<CustomerPortalSessionResponse>;
56
+ /**
57
+ * get product info
58
+ *
59
+ * @param productId product id
60
+ * @returns product info response
61
+ */
62
+ getProduct(productId: string): Promise<GetProductResponse>;
63
+ /**
64
+ * get products list
65
+ *
66
+ * @returns list of product info response
67
+ */
68
+ getProductList(params?: PageQuery): Promise<ListResult<GetProductResponse>>;
69
+ /**
70
+ * get price info
71
+ *
72
+ * @param priceId price id
73
+ * @returns price info response
74
+ */
75
+ getPrice(priceId: string): Promise<GetPriceResponse>;
76
+ /**
77
+ * get all prices
78
+ *
79
+ * @returns list of price info response
80
+ */
81
+ getPriceList(params: GetPriceRequest): Promise<ListResult<GetPriceResponse>>;
82
+ /**
83
+ * get payment instrument list
84
+ *
85
+ * @param params PaymentInstrumentRequest
86
+ * @returns list of payment instrument response. if not found, return empty list
87
+ */
88
+ getPaymentInstrumentList(params: PaymentInstrumentRequest): Promise<PaymentInstrumentResponse[]>;
89
+ }
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.ClinkPayClient = void 0;
13
+ const request_1 = require("./request");
14
+ const supportedEnvs = ['production', 'sandbox'];
15
+ class ClinkPayClient {
16
+ constructor(options) {
17
+ this.baseUrl = '';
18
+ const { apiKey, env = 'production' } = options;
19
+ if (!supportedEnvs.includes(env)) {
20
+ throw new Error(`env ${env} is not supported. supported envs are ${supportedEnvs}`);
21
+ }
22
+ this.apiKey = apiKey;
23
+ switch (env) {
24
+ case 'production': {
25
+ this.baseUrl = 'https://api.clinkbill.com/api';
26
+ break;
27
+ }
28
+ case 'sandbox': {
29
+ this.baseUrl = 'https://uat-api.clinkbill.com/api';
30
+ break;
31
+ }
32
+ }
33
+ this.requestClient = (0, request_1.createRequestClient)(this.baseUrl, this.apiKey);
34
+ }
35
+ /**
36
+ * create checkout session
37
+ *
38
+ * @param options CreateCheckoutSessionRequest
39
+ * @returns sessionResponse
40
+ */
41
+ createCheckoutSession(options) {
42
+ return __awaiter(this, void 0, void 0, function* () {
43
+ return this.requestClient.Post('/checkout/session', options);
44
+ });
45
+ }
46
+ /**
47
+ * get checkout session
48
+ *
49
+ * @param sessionId checkout session id
50
+ * @returns checkout session response
51
+ */
52
+ getCheckoutSession(sessionId) {
53
+ return __awaiter(this, void 0, void 0, function* () {
54
+ return this.requestClient.Get(`/checkout/session/${sessionId}`);
55
+ });
56
+ }
57
+ /**
58
+ * get order info
59
+ *
60
+ * @param orderId order id
61
+ * @returns order info response
62
+ */
63
+ getOrder(orderId) {
64
+ return __awaiter(this, void 0, void 0, function* () {
65
+ return this.requestClient.Get(`/order/${orderId}`);
66
+ });
67
+ }
68
+ /**
69
+ * get refund info
70
+ *
71
+ * @param refundId refund id
72
+ * @returns refund info response
73
+ */
74
+ getRefund(refundId) {
75
+ return __awaiter(this, void 0, void 0, function* () {
76
+ return this.requestClient.Get(`/refund/${refundId}`);
77
+ });
78
+ }
79
+ /**
80
+ * get subscription info
81
+ *
82
+ * @param subscriptionId subscription id
83
+ * @returns subscription info response
84
+ */
85
+ getSubscription(subscriptionId) {
86
+ return __awaiter(this, void 0, void 0, function* () {
87
+ return this.requestClient.Get(`/subscription/${subscriptionId}`);
88
+ });
89
+ }
90
+ /**
91
+ * get invoice info
92
+ *
93
+ * @param invoiceId invoice id
94
+ * @returns invoice info response
95
+ */
96
+ getInvoice(invoiceId) {
97
+ return __awaiter(this, void 0, void 0, function* () {
98
+ return this.requestClient.Get(`/subscription/invoice/${invoiceId}`);
99
+ });
100
+ }
101
+ /**
102
+ * create customer portal session
103
+ *
104
+ * @param options CustomerPortalSessionRequest
105
+ * @returns customer portal session response
106
+ */
107
+ customerPortalSession(options) {
108
+ return __awaiter(this, void 0, void 0, function* () {
109
+ return this.requestClient.Post('/billing/session', options);
110
+ });
111
+ }
112
+ /**
113
+ * get product info
114
+ *
115
+ * @param productId product id
116
+ * @returns product info response
117
+ */
118
+ getProduct(productId) {
119
+ return __awaiter(this, void 0, void 0, function* () {
120
+ return this.requestClient.Get(`/product/${productId}`);
121
+ });
122
+ }
123
+ /**
124
+ * get products list
125
+ *
126
+ * @returns list of product info response
127
+ */
128
+ getProductList(params) {
129
+ return __awaiter(this, void 0, void 0, function* () {
130
+ return this.requestClient.Get(`/product`, {
131
+ params,
132
+ });
133
+ });
134
+ }
135
+ /**
136
+ * get price info
137
+ *
138
+ * @param priceId price id
139
+ * @returns price info response
140
+ */
141
+ getPrice(priceId) {
142
+ return __awaiter(this, void 0, void 0, function* () {
143
+ return this.requestClient.Get(`/price/${priceId}`);
144
+ });
145
+ }
146
+ /**
147
+ * get all prices
148
+ *
149
+ * @returns list of price info response
150
+ */
151
+ getPriceList(params) {
152
+ return __awaiter(this, void 0, void 0, function* () {
153
+ return this.requestClient.Get(`/price`, {
154
+ params,
155
+ });
156
+ });
157
+ }
158
+ /**
159
+ * get payment instrument list
160
+ *
161
+ * @param params PaymentInstrumentRequest
162
+ * @returns list of payment instrument response. if not found, return empty list
163
+ */
164
+ getPaymentInstrumentList(params) {
165
+ return __awaiter(this, void 0, void 0, function* () {
166
+ const { customerId, type } = params;
167
+ return this.requestClient.Get(`/payment_instrument/list?customerId=${customerId}&type=${type}`);
168
+ });
169
+ }
170
+ }
171
+ exports.ClinkPayClient = ClinkPayClient;
@@ -0,0 +1,399 @@
1
+ export type ClinkEnv = 'sandbox' | 'production';
2
+ export interface ClinkPayClientConstructorOptions {
3
+ apiKey: string;
4
+ /**
5
+ * default to production
6
+ */
7
+ env?: ClinkEnv;
8
+ }
9
+ export interface CommonResponse<T = any> {
10
+ code: number;
11
+ msg: string;
12
+ data: T;
13
+ }
14
+ export interface CustomerPortalSessionRequest {
15
+ /**
16
+ * Existing customer's unique identifier
17
+ */
18
+ customerId: string;
19
+ /**
20
+ * Redirect URL when customer click on 'Return to Merchant'
21
+ */
22
+ returnUrl?: string;
23
+ }
24
+ export interface CustomerPortalSessionResponse {
25
+ /**
26
+ * The magic link for customer to access the portal
27
+ */
28
+ url: string;
29
+ /**
30
+ * Link expiry time
31
+ */
32
+ expiresAt: number;
33
+ }
34
+ export interface SessionProduct {
35
+ /**
36
+ * Display name of the product shown to customers during checkout
37
+ */
38
+ name: string;
39
+ /**
40
+ * Price per unit in the specified currency (must be greater than 0)
41
+ */
42
+ unitAmount: number;
43
+ /**
44
+ * Number of units to be purchased (minimum 1)
45
+ */
46
+ quantity?: number;
47
+ /**
48
+ * Three-letter ISO currency code for the price (e.g., USD, EUR, GBP)
49
+ */
50
+ currency?: string;
51
+ /**
52
+ * URL of the product image to display during checkout
53
+ */
54
+ imageUrl?: string;
55
+ }
56
+ export interface CreateCheckoutSessionRequest {
57
+ /**
58
+ * Total transaction amount in the specified currency (must be greater than 0)
59
+ */
60
+ originalAmount: number;
61
+ /**
62
+ * Three-letter ISO currency code for the transaction (e.g., USD, EUR, GBP)
63
+ */
64
+ originalCurrency: string;
65
+ /**
66
+ * Existing customer's unique identifier (required if customerEmail is not provided)
67
+ */
68
+ customerId?: string;
69
+ /**
70
+ * Customer's email address. A new customer account will be created if the email is not associated with an existing customer
71
+ */
72
+ customerEmail?: string;
73
+ /**
74
+ * Your internal reference ID for tracking (non-unique identifier, does not guarantee idempotency)
75
+ */
76
+ merchantReferenceId?: string;
77
+ /**
78
+ * Unique identifier of the predefined price configuration from your dashboard
79
+ */
80
+ priceId?: string;
81
+ /**
82
+ * Unique identifier of the product configured in your dashboard
83
+ */
84
+ productId?: string;
85
+ /**
86
+ * URL where customers will be redirected after successful payment completion
87
+ */
88
+ successUrl?: string;
89
+ /**
90
+ * URL where customers will be redirected if they cancel the checkout process
91
+ */
92
+ cancelUrl?: string;
93
+ /**
94
+ * List of product pricing details for one-time purchases. Use this when creating transactions without pre-configured products
95
+ */
96
+ priceDataList?: SessionProduct[];
97
+ /**
98
+ * Whether to allow promotion codes to be applied to the transaction (default: false)
99
+ */
100
+ allowPromotionCodes?: boolean;
101
+ }
102
+ export interface CreateCheckoutSessionResponse {
103
+ /**
104
+ * Unique identifier for the created checkout session
105
+ */
106
+ sessionId: string;
107
+ /**
108
+ * Token value required for payment processing. This token is used to verify the payment status and complete the transaction
109
+ */
110
+ tokenValue: string;
111
+ /**
112
+ * Customer's unique identifier associated with the checkout session
113
+ */
114
+ customerId: string;
115
+ /**
116
+ * Original transaction amount in the specified currency
117
+ */
118
+ originalAmount: number;
119
+ /**
120
+ * Three-letter ISO currency code for the transaction (e.g., USD, EUR, GBP)
121
+ */
122
+ originalCurrency: string;
123
+ /**
124
+ * Symbol of the currency (e.g., $, €, £)
125
+ */
126
+ originalCurrencySymbol: string;
127
+ /**
128
+ * checkout url
129
+ */
130
+ url: string;
131
+ merchantReferenceId: string;
132
+ /**
133
+ * Expiration time
134
+ */
135
+ expireTime: string;
136
+ }
137
+ export declare namespace GetSessionResponse {
138
+ interface PriceList {
139
+ amount: number;
140
+ currency: string;
141
+ exchangeRate: number;
142
+ }
143
+ interface Recurring {
144
+ freeTrialDays: number;
145
+ interval: string;
146
+ }
147
+ interface Price {
148
+ priceId: string;
149
+ priceList: PriceList[];
150
+ recurring: Recurring;
151
+ }
152
+ interface Customer {
153
+ customerId: string;
154
+ email: string;
155
+ }
156
+ interface Product {
157
+ productId: string;
158
+ productName: string;
159
+ }
160
+ interface SessionInfo {
161
+ sessionId: string;
162
+ token: string;
163
+ status: string;
164
+ paymentStatus: string;
165
+ amountSubtotal: number;
166
+ amountTotal?: number;
167
+ originalCurrency: string;
168
+ paymentCurrency?: string;
169
+ subscriptionId?: string;
170
+ invoiceId?: string;
171
+ orderId?: string;
172
+ price: Price;
173
+ merchantReferenceId?: string;
174
+ customer: Customer;
175
+ locale: string;
176
+ cancelUrl?: string;
177
+ successUrl?: string;
178
+ created: string;
179
+ expire: string;
180
+ product: Product;
181
+ priceDataList?: SessionProduct[];
182
+ }
183
+ }
184
+ export interface GetOrderResponse {
185
+ orderId: string;
186
+ merchantReferenceId?: string;
187
+ invoiceId: string;
188
+ customerId: string;
189
+ productId: string;
190
+ priceId: string;
191
+ priceDataList?: SessionProduct[];
192
+ paymentMethod: {
193
+ paymentMethodType: string;
194
+ paymentInstrumentId: string;
195
+ };
196
+ amountSubtotal: number;
197
+ amountTotal: number;
198
+ paymentCurrency: string;
199
+ originalCurrency: string;
200
+ status: string;
201
+ }
202
+ export declare namespace GetSubscriptionResponse {
203
+ interface Recurring {
204
+ interval: string;
205
+ trialPeriodDays: number;
206
+ pricingModel: string;
207
+ tiersMode?: any;
208
+ }
209
+ interface Price {
210
+ productId: string;
211
+ productName: string;
212
+ priceId: string;
213
+ priceSnapshotId: string;
214
+ unitAmount: string;
215
+ quantity: number;
216
+ recurring: Recurring;
217
+ }
218
+ interface RecurringInvoiceItem {
219
+ invoiceItemId: string;
220
+ amount: string;
221
+ currency: string;
222
+ description: string;
223
+ periodStart: number;
224
+ periodEnd: number;
225
+ proration?: number;
226
+ price: Price;
227
+ }
228
+ interface Data {
229
+ merchantReference?: any;
230
+ subscriptionId: string;
231
+ customerId: string;
232
+ productId: string;
233
+ priceId: string;
234
+ priceSnapshotId: string;
235
+ createTime: number;
236
+ quantity: number;
237
+ paymentMethodType: string;
238
+ paymentInstrumentId: string;
239
+ trialStart?: string;
240
+ trialEnd?: string;
241
+ currentPeriodStart: number;
242
+ currentPeriodEnd: number;
243
+ cancelAt?: string;
244
+ cancelAtPeriodEnd?: string;
245
+ canceledAt?: string;
246
+ cancelReason?: string;
247
+ status: string;
248
+ billing: string;
249
+ currency: string;
250
+ recurringInvoiceItem: RecurringInvoiceItem;
251
+ upcomingInvoiceItem?: RecurringInvoiceItem;
252
+ }
253
+ }
254
+ export declare namespace GetInvoiceResponse {
255
+ interface Recurring {
256
+ interval: string;
257
+ trialPeriodDays: number;
258
+ pricingModel: string;
259
+ tiersMode?: any;
260
+ }
261
+ interface Price {
262
+ productId: string;
263
+ productName: string;
264
+ priceId: string;
265
+ priceSnapshotId: string;
266
+ unitAmount: string;
267
+ quantity: number;
268
+ recurring: Recurring;
269
+ }
270
+ interface Item {
271
+ invoiceItemId: string;
272
+ amount: string;
273
+ currency: string;
274
+ description: string;
275
+ periodStart: number;
276
+ periodEnd: number;
277
+ proration?: any;
278
+ price: Price;
279
+ }
280
+ interface Data {
281
+ invoiceId: string;
282
+ subscriptionId: string;
283
+ orderId: string;
284
+ customerId: string;
285
+ merchantId: string;
286
+ status: string;
287
+ createTime: number;
288
+ currentPeriodStart: number;
289
+ currentPeriodEnd: number;
290
+ originalAmount: string;
291
+ paymentAmount: string;
292
+ originalCurrency: string;
293
+ billing: string;
294
+ items: Item[];
295
+ discount?: number;
296
+ }
297
+ }
298
+ export interface RefundResponse {
299
+ createTime: number;
300
+ refundId: string;
301
+ orderId: string;
302
+ customerId: string;
303
+ refundAmount: number;
304
+ refundCurrency: string;
305
+ status: string;
306
+ refundReason: string;
307
+ paymentInstrumentId: string;
308
+ metadata?: string;
309
+ }
310
+ export interface ListResult<T> {
311
+ total: number;
312
+ rows: T[];
313
+ }
314
+ export interface PageQuery {
315
+ /**
316
+ * Page number
317
+ */
318
+ page?: number;
319
+ /**
320
+ * Number of items per page
321
+ */
322
+ pageSize?: number;
323
+ }
324
+ export interface GetProductResponse {
325
+ productId: string;
326
+ name: string;
327
+ active: boolean;
328
+ description: string;
329
+ defaultPrice: string;
330
+ image: string;
331
+ taxCategory: string;
332
+ createTime: number;
333
+ updateTime?: string;
334
+ }
335
+ export interface GetPriceRequest extends PageQuery {
336
+ /**
337
+ * Product ID
338
+ */
339
+ productId: string;
340
+ /**
341
+ * Is it available for purchase
342
+ * @requires active
343
+ */
344
+ active: boolean;
345
+ }
346
+ export interface GetPriceResponse {
347
+ priceId: string;
348
+ productId: string;
349
+ currency: string;
350
+ unitAmount: number;
351
+ priceType: string;
352
+ recurring: {
353
+ interval: string;
354
+ tiers?: any;
355
+ interval_count?: any;
356
+ trial_period_days: number;
357
+ pricing_model: string;
358
+ tiers_mode?: any;
359
+ transform_quantity?: any;
360
+ [key: string]: any;
361
+ };
362
+ active: boolean;
363
+ createTime: number;
364
+ }
365
+ export type PaymentType = 'CARD' | 'CASHAPP' | 'KAKAO' | 'WECHAT' | string;
366
+ export interface PaymentInstrumentRequest {
367
+ customerId: string;
368
+ type: PaymentType;
369
+ }
370
+ export interface PaymentInstrumentResponse {
371
+ id: string;
372
+ customerId: string;
373
+ type: string;
374
+ card?: Card;
375
+ wallet?: Wallet;
376
+ created: number;
377
+ }
378
+ export interface Wallet {
379
+ accountTag: string;
380
+ }
381
+ export interface BillingAddress {
382
+ city?: string;
383
+ country: string;
384
+ line1?: string;
385
+ line2?: string;
386
+ postalCode?: string;
387
+ state?: string;
388
+ }
389
+ export interface Card {
390
+ last4: string;
391
+ name: string;
392
+ expiryYear: string;
393
+ expiryMonth: string;
394
+ scheme: string;
395
+ funding: string;
396
+ issuerRegion: string;
397
+ issuerBank: string;
398
+ billingAddress: BillingAddress;
399
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ export declare function createRequestClient(baseURL: string, apiKey: string): import("alova").Alova<import("alova").AlovaGenerics<any, any, import("alova/fetch").FetchRequestInit, Response, Headers, import("alova").AlovaDefaultCacheAdapter, import("alova").AlovaDefaultCacheAdapter, import("alova").StatesExport<any>>>;
2
+ export type RequestClient = ReturnType<typeof createRequestClient>;
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __rest = (this && this.__rest) || function (s, e) {
12
+ var t = {};
13
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
14
+ t[p] = s[p];
15
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
16
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
17
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
18
+ t[p[i]] = s[p[i]];
19
+ }
20
+ return t;
21
+ };
22
+ var __importDefault = (this && this.__importDefault) || function (mod) {
23
+ return (mod && mod.__esModule) ? mod : { "default": mod };
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.createRequestClient = createRequestClient;
27
+ const alova_1 = require("alova");
28
+ const fetch_1 = __importDefault(require("alova/fetch"));
29
+ const exceptions_1 = require("../exceptions");
30
+ /**
31
+ * Success code for API requests
32
+ */
33
+ const SUCCESS_CODE = 200;
34
+ function createRequestClient(baseURL, apiKey) {
35
+ const alovaInstance = (0, alova_1.createAlova)({
36
+ baseURL,
37
+ requestAdapter: (0, fetch_1.default)(),
38
+ responded: {
39
+ onError: (error) => {
40
+ throw error;
41
+ },
42
+ onSuccess: (response) => __awaiter(this, void 0, void 0, function* () {
43
+ // Most likely due to network issues causing errors
44
+ if (response.status >= 400) {
45
+ try {
46
+ const json = (yield response.json());
47
+ throw new exceptions_1.ClinkApiError(json.code, json.msg);
48
+ }
49
+ catch (e) {
50
+ if (e instanceof exceptions_1.ClinkApiError) {
51
+ throw e;
52
+ }
53
+ // throw if response.json failed
54
+ throw new Error(response.statusText);
55
+ }
56
+ }
57
+ const json = (yield response.json());
58
+ if (json.code !== SUCCESS_CODE) {
59
+ throw new exceptions_1.ClinkApiError(json.code, json.msg);
60
+ }
61
+ const { code, msg, data } = json, rest = __rest(json, ["code", "msg", "data"]);
62
+ // Compatible with non-standard return formats for lists
63
+ if (data !== undefined) {
64
+ return data;
65
+ }
66
+ return rest;
67
+ }),
68
+ },
69
+ beforeRequest(method) {
70
+ method.config.headers['Content-Type'] = 'application/json';
71
+ // locale en
72
+ method.config.headers['Accept-Language'] = 'en_US';
73
+ method.config.headers['Content-Language'] = 'en_US';
74
+ // add secret key to header
75
+ method.config.headers['X-API-Key'] = apiKey;
76
+ method.config.headers['X-TIMESTAMP'] = Date.now().toString();
77
+ },
78
+ cacheFor: null,
79
+ shareRequest: false,
80
+ timeout: 10000,
81
+ });
82
+ return alovaInstance;
83
+ }
@@ -0,0 +1,7 @@
1
+ export declare class ClinkApiError extends Error {
2
+ code: number;
3
+ constructor(code: number, message: string);
4
+ }
5
+ export declare class ClinkWebhookSignatureError extends Error {
6
+ constructor(message: string);
7
+ }
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ClinkWebhookSignatureError = exports.ClinkApiError = void 0;
4
+ class ClinkApiError extends Error {
5
+ constructor(code, message) {
6
+ super(message);
7
+ this.code = code;
8
+ }
9
+ }
10
+ exports.ClinkApiError = ClinkApiError;
11
+ class ClinkWebhookSignatureError extends Error {
12
+ constructor(message) {
13
+ super(message);
14
+ }
15
+ }
16
+ exports.ClinkWebhookSignatureError = ClinkWebhookSignatureError;
@@ -0,0 +1,3 @@
1
+ export * from './client';
2
+ export * from './exceptions';
3
+ export * from './webhook';
package/dist/index.js ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./client"), exports);
18
+ __exportStar(require("./exceptions"), exports);
19
+ __exportStar(require("./webhook"), exports);
@@ -0,0 +1,31 @@
1
+ import { ClinkWebhookConstructorOptions, ClinkWebhookEvent, ClinkWebhookSignatureOptions, WithHeaderSignature } from './model';
2
+ export declare class ClinkWebhook {
3
+ private signatureKey;
4
+ constructor(options: ClinkWebhookConstructorOptions);
5
+ /**
6
+ * generate webhook signature
7
+ *
8
+ * @param options { timestamp: string, body: string }
9
+ * @returns string
10
+ */
11
+ signature(options: ClinkWebhookSignatureOptions): string;
12
+ /**
13
+ * verify webhook signature
14
+ *
15
+ * @param options { timestamp: string, body: string, headerSignature: string }
16
+ * @returns boolean
17
+ */
18
+ verifySignature(options: WithHeaderSignature): boolean;
19
+ /**
20
+ * verify webhook signature and return event data
21
+ *
22
+ * @param options { timestamp: string, body: string, headerSignature: string }
23
+ * @throws ClinkWebhookSignatureError when signature verify failed
24
+ * @returns ClinkWebhookEvent<T>
25
+ */
26
+ verifyAndGet<T = any>(options: {
27
+ timestamp: string;
28
+ body: string;
29
+ headerSignature: string;
30
+ }): ClinkWebhookEvent<T>;
31
+ }
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ClinkWebhook = void 0;
7
+ const crypto_js_1 = __importDefault(require("crypto-js"));
8
+ const exceptions_1 = require("../exceptions");
9
+ class ClinkWebhook {
10
+ constructor(options) {
11
+ const { signatureKey } = options;
12
+ this.signatureKey = signatureKey;
13
+ }
14
+ /**
15
+ * generate webhook signature
16
+ *
17
+ * @param options { timestamp: string, body: string }
18
+ * @returns string
19
+ */
20
+ signature(options) {
21
+ const { timestamp, body } = options;
22
+ const payload = `${timestamp}.${body}`;
23
+ const hash = crypto_js_1.default.HmacSHA256(payload, this.signatureKey);
24
+ return crypto_js_1.default.enc.Hex.stringify(hash);
25
+ }
26
+ /**
27
+ * verify webhook signature
28
+ *
29
+ * @param options { timestamp: string, body: string, headerSignature: string }
30
+ * @returns boolean
31
+ */
32
+ verifySignature(options) {
33
+ const { timestamp, body, headerSignature } = options;
34
+ const payload = `${timestamp}.${body}`;
35
+ const hash = crypto_js_1.default.HmacSHA256(payload, this.signatureKey);
36
+ const computedSignature = crypto_js_1.default.enc.Hex.stringify(hash);
37
+ return computedSignature === headerSignature;
38
+ }
39
+ /**
40
+ * verify webhook signature and return event data
41
+ *
42
+ * @param options { timestamp: string, body: string, headerSignature: string }
43
+ * @throws ClinkWebhookSignatureError when signature verify failed
44
+ * @returns ClinkWebhookEvent<T>
45
+ */
46
+ verifyAndGet(options) {
47
+ const { timestamp, body, headerSignature } = options;
48
+ const isSignatureValid = this.verifySignature({
49
+ timestamp,
50
+ body,
51
+ headerSignature,
52
+ });
53
+ if (!isSignatureValid) {
54
+ throw new exceptions_1.ClinkWebhookSignatureError('webhook signature verify failed');
55
+ }
56
+ return JSON.parse(body);
57
+ }
58
+ }
59
+ exports.ClinkWebhook = ClinkWebhook;
@@ -0,0 +1,34 @@
1
+ export interface ClinkWebhookConstructorOptions {
2
+ /**
3
+ * you can get signatureKey from dashboard -> Developers -> webhook
4
+ */
5
+ signatureKey: string;
6
+ }
7
+ /**
8
+ * common webhook event
9
+ */
10
+ export interface ClinkWebhookEvent<T = any> {
11
+ id: string;
12
+ type: string;
13
+ object: string;
14
+ created: string;
15
+ data: {
16
+ object: T;
17
+ };
18
+ }
19
+ export interface ClinkWebhookSignatureOptions {
20
+ /**
21
+ * timestamp from header X-Clink-Timestamp
22
+ */
23
+ timestamp: string;
24
+ /**
25
+ * body from request body
26
+ */
27
+ body: string;
28
+ }
29
+ export interface WithHeaderSignature extends ClinkWebhookSignatureOptions {
30
+ /**
31
+ * signature from header X-Clink-Signature
32
+ */
33
+ headerSignature: string;
34
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "clink-typescript-sdk-test",
3
+ "version": "0.0.3",
4
+ "author": {
5
+ "name": "Clink-test",
6
+ "email": "support@clinkbill.com",
7
+ "url": "https://www.clinkbill.com/"
8
+ },
9
+ "description": "npm package for clinkbill",
10
+ "keywords": [
11
+ "clinkbill",
12
+ "clinkpay"
13
+ ],
14
+ "main": "dist/index.js",
15
+ "types": "dist/index.d.ts",
16
+ "scripts": {
17
+ "build": "tsc",
18
+ "test:api": "vitest test/api.test.ts",
19
+ "test:webhook": "vitest test/webhook.test.ts",
20
+ "test": "vitest */*.test.ts",
21
+ "clean-dist": "rm -rf ./dist"
22
+ },
23
+ "files": [
24
+ "dist"
25
+ ],
26
+ "license": "MIT",
27
+ "dependencies": {
28
+ "alova": "3.3.4",
29
+ "crypto-js": "4.2.0"
30
+ },
31
+ "devDependencies": {
32
+ "@types/crypto-js": "^4.2.2",
33
+ "@types/node": "24.10.1",
34
+ "prettier": "3.6.2",
35
+ "typescript": "^5",
36
+ "vitest": "4.0.13"
37
+ },
38
+ "engines": {
39
+ "node": ">=18"
40
+ }
41
+ }