@tapsilat/tapsilat-js 2026.7.7 → 2026.10.26

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.
Files changed (50) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +217 -46
  3. package/dist/TapsilatSDK.d.ts +158 -40
  4. package/dist/TapsilatSDK.d.ts.map +1 -1
  5. package/dist/__mocks__/HttpClient.d.ts +1 -0
  6. package/dist/__mocks__/HttpClient.d.ts.map +1 -1
  7. package/dist/errors/TapsilatError.d.ts +12 -1
  8. package/dist/errors/TapsilatError.d.ts.map +1 -1
  9. package/dist/http/HttpClient.d.ts +19 -1
  10. package/dist/http/HttpClient.d.ts.map +1 -1
  11. package/dist/http/interceptors.d.ts.map +1 -1
  12. package/dist/index.cjs +1442 -128
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.ts +2029 -133
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +1429 -129
  17. package/dist/index.js.map +1 -1
  18. package/dist/services/MarketplaceService.d.ts +59 -0
  19. package/dist/services/MarketplaceService.d.ts.map +1 -0
  20. package/dist/services/ServiceBase.d.ts +21 -0
  21. package/dist/services/ServiceBase.d.ts.map +1 -0
  22. package/dist/services/SubmerchantService.d.ts +31 -0
  23. package/dist/services/SubmerchantService.d.ts.map +1 -0
  24. package/dist/services/TokenizationService.d.ts +14 -0
  25. package/dist/services/TokenizationService.d.ts.map +1 -0
  26. package/dist/services/VposService.d.ts +26 -0
  27. package/dist/services/VposService.d.ts.map +1 -0
  28. package/dist/services/index.d.ts +6 -0
  29. package/dist/services/index.d.ts.map +1 -0
  30. package/dist/types/elements.d.ts +144 -0
  31. package/dist/types/elements.d.ts.map +1 -0
  32. package/dist/types/index.d.ts +90 -44
  33. package/dist/types/index.d.ts.map +1 -1
  34. package/dist/types/panel.d.ts +839 -0
  35. package/dist/types/panel.d.ts.map +1 -0
  36. package/dist/utils/currency.d.ts +25 -0
  37. package/dist/utils/currency.d.ts.map +1 -0
  38. package/dist/utils/redact.d.ts +16 -0
  39. package/dist/utils/redact.d.ts.map +1 -0
  40. package/dist/utils/response.d.ts +12 -0
  41. package/dist/utils/response.d.ts.map +1 -1
  42. package/dist/utils/validators.d.ts +12 -0
  43. package/dist/utils/validators.d.ts.map +1 -1
  44. package/dist/utils/verify.d.ts +31 -8
  45. package/dist/utils/verify.d.ts.map +1 -1
  46. package/dist/webhooks/OrderStatus.d.ts +98 -0
  47. package/dist/webhooks/OrderStatus.d.ts.map +1 -0
  48. package/dist/webhooks/WebhookEvent.d.ts +58 -0
  49. package/dist/webhooks/WebhookEvent.d.ts.map +1 -0
  50. package/package.json +7 -7
package/CHANGELOG.md CHANGED
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2026.10.26] - 2026-10-26
11
+
12
+ ### Added
13
+ - `createElementsSession`, `getElementsSession` and `cancelElementsSession` for Tapsilat Elements sessions (`POST /elements/sessions`, `GET /elements/sessions/{id}`, `POST /elements/sessions/{id}/cancel`), with the `ElementsSessionCreateRequest`, `ElementsSessionCreateResponse`, `ElementsSession`, `ElementsCardSummary` and `ElementsSessionError` types and the `ElementsSessionPurpose`, `ElementsSessionStatus`, `ElementsTokenizeMode` and `ElementsCardBrand` constants. In Node.js the created session prints its `client_secret` as `[redacted]`; `JSON.stringify` keeps it.
14
+ - `TapsilatError.retryable`, set when the API error says the request can be retried.
15
+
16
+ ### Fixed
17
+ - Errors nested as `{"error": {"code", "message", "retryable"}}` set `message`, `apiCode` and `retryable` from that object instead of a message of `[object Object]`.
18
+
10
19
  ## [2025.12.5] - 2025-12-05
11
20
 
12
21
  ### Added
package/README.md CHANGED
@@ -75,6 +75,7 @@ console.log("Order status:", status.status);
75
75
  - Complete payment lifecycle management
76
76
  - Multi-currency support (TRY, USD, EUR, GBP)
77
77
  - Advanced filtering and pagination
78
+ - Tapsilat Elements sessions for the native Android and iOS card sheet
78
79
 
79
80
  ### Payment Term Management
80
81
  - Create and manage installment plans
@@ -101,19 +102,7 @@ console.log("Order status:", status.status);
101
102
 
102
103
  ## SDK Compatibility
103
104
 
104
- This JavaScript SDK provides full feature parity with Tapsilat's Python and .NET SDKs:
105
-
106
- | Feature Category | JavaScript | Python | .NET |
107
- |-----------------|------------|--------|------|
108
- | Order Management | Yes | Yes | Yes |
109
- | Payment Terms | Yes | Yes | Yes |
110
- | System Utilities | Yes | Yes | Yes |
111
- | Organization | Yes | Yes | Yes |
112
- | GSM Validation | Yes | Yes | Yes |
113
- | Installment Validation | Yes | Yes | Yes |
114
- | Webhook Verification | Yes | No | No |
115
- | TypeScript Support | Yes | No | Yes |
116
- | Health Monitoring | Yes | No | No |
105
+ All Tapsilat SDKs (Go, JavaScript, Python, .NET, Kotlin, Rust, Swift and Zig) target the same panel REST API, so orders, payment terms, subscriptions, organization settings, submerchants, VPOS, card tokenization, card installments and the marketplace API behave the same in each. Webhook signature verification and the health check are additional helpers in this SDK.
117
106
 
118
107
  ---
119
108
 
@@ -152,7 +141,7 @@ console.log('Order status:', order.status);
152
141
  #### Check Order Status
153
142
  ```typescript
154
143
  const status = await tapsilat.getOrderStatus('order-reference-id');
155
- if (status.status === 'completed') {
144
+ if (OrderStatus.isPaid(status.status)) {
156
145
  console.log('Payment successful!');
157
146
  }
158
147
  ```
@@ -193,6 +182,22 @@ transactions.forEach(tx => {
193
182
  });
194
183
  ```
195
184
 
185
+ #### Charge Order
186
+ ```typescript
187
+ const chargeResult = await tapsilat.chargeOrder({
188
+ order_reference_id: 'order-reference-id'
189
+ });
190
+ console.log('Order charged:', chargeResult);
191
+ ```
192
+
193
+ #### Get Order Payments
194
+ `getAllOrdersPayments` is deprecated: the API has no route for it. List one order's payments with `getOrderPayments` and an order ID, reference ID or conversation ID.
195
+
196
+ ```typescript
197
+ const payments = await tapsilat.getOrderPayments({ order_reference_id: 'order-reference-id' });
198
+ console.log('Merchant net amount:', payments.total_merchant_net_amount);
199
+ ```
200
+
196
201
  #### Get Checkout URL
197
202
  ```typescript
198
203
  const checkoutUrl = await tapsilat.getCheckoutUrl('order-reference-id');
@@ -225,55 +230,65 @@ console.log('Full refund processed:', fullRefund.amount);
225
230
  ### Webhook Handling
226
231
 
227
232
  #### Verify Webhook Signature
233
+ Tapsilat signs every callback it sends to your callback URLs, and to an order's `payment_success_url` and `payment_failure_url`, with the webhook secret from the panel's callback settings. The `Tapsilat-Signature` header looks like `t=1726480000,v1=5257a8…`: `v1` is the hex HMAC-SHA256 of the timestamp, a dot and the raw body.
234
+
235
+ `verifyWebhook` checks it in constant time and rejects a timestamp more than 5 minutes from the server clock, so a captured callback cannot be replayed. Pass the raw body (a string or a `Buffer`), not re-encoded JSON. It resolves to `false` when the header is missing, malformed, stale or wrong.
228
236
  ```typescript
229
237
  import express from 'express';
238
+ import { WebhookEvent, WEBHOOK_SIGNATURE_HEADER } from '@tapsilat/tapsilat-js';
230
239
 
231
240
  const app = express();
232
- app.use(express.raw({ type: 'application/json' }));
233
241
 
234
- app.post('/webhooks/tapsilat', async (req, res) => {
235
- const payload = req.body.toString();
236
- const signature = req.headers['x-tapsilat-signature'];
237
-
242
+ app.post('/webhooks/tapsilat', express.raw({ type: '*/*' }), async (req, res) => {
238
243
  const isValid = await tapsilat.verifyWebhook(
239
- payload,
240
- signature,
241
- process.env.WEBHOOK_SECRET
244
+ req.body, // the raw body as a Buffer
245
+ req.get(WEBHOOK_SIGNATURE_HEADER), // "Tapsilat-Signature"
246
+ process.env.TAPSILAT_WEBHOOK_SECRET!
242
247
  );
243
-
244
248
  if (!isValid) {
245
- return res.status(401).json({ error: 'Invalid signature' });
249
+ return res.sendStatus(400);
246
250
  }
247
-
248
- const event = JSON.parse(payload);
249
-
250
- switch (event.type) {
251
- case 'order.completed':
252
- console.log('Order completed:', event.data.reference_id);
253
- // Process successful payment
254
- break;
255
- case 'order.failed':
256
- console.log('Order failed:', event.data.reference_id);
257
- // Handle failed payment
258
- break;
251
+
252
+ const event = WebhookEvent.fromPayload(req.body);
253
+ event.type; // "order_lite" (the default), "order", "order_payment_detail", ...
254
+ event.referenceId; // the order's reference ID, whatever the callback type
255
+ event.conversationId;
256
+ event.status; // e.g. "Paid", when the callback type carries a status
257
+
258
+ if (event.isPaid()) {
259
+ // fulfil the order
259
260
  }
260
-
261
- res.json({ received: true });
261
+ res.sendStatus(200);
262
262
  });
263
263
  ```
264
264
 
265
+ A different tolerance in seconds can be passed as `{ tolerance }` in the fourth argument (`0` disables the timestamp check), and `{ now }` sets the Unix time to compare with. A bare hex HMAC of the body, with or without a `sha256=` prefix, is still accepted for signatures made the way earlier SDK versions documented.
266
+
267
+ #### Order Statuses
268
+ `OrderStatus` has the numeric status codes that callbacks carry, the names `getOrderStatus()` returns, and the groups most integrations need:
269
+ ```typescript
270
+ import { OrderStatus } from '@tapsilat/tapsilat-js';
271
+
272
+ const { status } = await tapsilat.getOrderStatus(referenceId); // e.g. "Waiting for payment"
273
+
274
+ OrderStatus.isPaid(status); // Paid, Completed, Sub merchant payment approved
275
+ OrderStatus.isFailed(status); // Cancelled, Fraud, Rejected, Failure, Expired, Terminated
276
+ OrderStatus.isRefunded(status); // Refunded, Partially refunded
277
+ OrderStatus.code(status); // 7; also accepts "7" or 7
278
+ ```
279
+
265
280
  ### Payment Term Management
266
281
 
267
282
  #### Create Payment Term
268
283
  ```typescript
284
+ // order_id is the order's ID (order_id from createOrder), not its reference ID
269
285
  const paymentTerm = await tapsilat.createOrderTerm({
270
- order_id: 'order-reference-id',
286
+ order_id: createdOrder.order_id,
271
287
  term_reference_id: 'term-001',
272
288
  amount: 250.00,
273
- due_date: '2024-12-31',
289
+ due_date: '2024-12-31', // YYYY-MM-DD
274
290
  term_sequence: 1,
275
291
  required: true,
276
- status: 'pending',
277
292
  data: 'First installment'
278
293
  });
279
294
  console.log('Payment term created:', paymentTerm.term_reference_id);
@@ -281,11 +296,14 @@ console.log('Payment term created:', paymentTerm.term_reference_id);
281
296
 
282
297
  #### Update Payment Term
283
298
  ```typescript
299
+ // The update replaces the term, so send every field rather than only the changed ones
284
300
  const updatedTerm = await tapsilat.updateOrderTerm({
285
301
  term_reference_id: 'term-001',
302
+ term_sequence: 1,
303
+ required: true,
286
304
  amount: 275.00,
287
305
  due_date: '2025-01-15',
288
- status: 'updated'
306
+ status: 'pending'
289
307
  });
290
308
  console.log('Payment term updated:', updatedTerm.status);
291
309
  ```
@@ -293,6 +311,7 @@ console.log('Payment term updated:', updatedTerm.status);
293
311
  #### Delete Payment Term
294
312
  ```typescript
295
313
  const deletedTerm = await tapsilat.deleteOrderTerm({
314
+ order_id: createdOrder.order_id,
296
315
  term_reference_id: 'term-001'
297
316
  });
298
317
  console.log('Payment term deleted:', deletedTerm.term_reference_id);
@@ -301,14 +320,16 @@ console.log('Payment term deleted:', deletedTerm.term_reference_id);
301
320
  #### Refund Payment Term
302
321
  ```typescript
303
322
  const termRefund = await tapsilat.refundOrderTerm({
304
- term_id: 'term-001',
305
- amount: 100.00,
306
- reference_id: 'refund-ref-123'
323
+ term_id: 'term-id', // the term's ID
324
+ reference_id: 'order-reference-id', // the order's reference ID
325
+ amount: 100.00
307
326
  });
308
327
  console.log('Term refund processed:', termRefund.refund_id);
309
328
  ```
310
329
 
311
330
  #### Terminate Payment Term
331
+ `terminateOrderTerm` is deprecated: the API has no payment term termination route. Delete the term with `deleteOrderTerm`, or end the whole order with `terminateOrder`.
332
+
312
333
  ```typescript
313
334
  const terminatedTerm = await tapsilat.terminateOrderTerm({
314
335
  term_reference_id: 'term-001',
@@ -478,7 +499,21 @@ console.log('Organization Settings:', settings);
478
499
  #### Get Currency Presets
479
500
  ```typescript
480
501
  const presets = await tapsilat.getOrganizationCurrencyPresets();
481
- console.log('Available currencies:', presets);
502
+ console.log('Available currencies:', presets.items);
503
+ ```
504
+
505
+ #### Organization Management
506
+ ```typescript
507
+ // Create Currency
508
+ const newCurrency = await tapsilat.createOrganizationCurrency({
509
+ currency_code: 'EUR'
510
+ });
511
+
512
+ // Get Partners
513
+ const partners = await tapsilat.getOrganizationPartners();
514
+
515
+ // Get Limits
516
+ const limits = await tapsilat.getOrganizationLimitsById('org-uuid', { currency: 'TRY' });
482
517
  ```
483
518
 
484
519
  #### Get Suborganization Details
@@ -492,6 +527,7 @@ console.log('Suborganization Name:', suborg.name);
492
527
  #### Get System Endpoints
493
528
  ```typescript
494
529
  // Fetch various system configurations
530
+ const config = await tapsilat.getSystemConfig();
495
531
  const errorCodes = await tapsilat.getSystemErrorCodes();
496
532
  const productTypes = await tapsilat.getSystemProductTypes();
497
533
  const transactionStatuses = await tapsilat.getSystemTransactionStatuses();
@@ -520,6 +556,141 @@ const updateResult = await tapsilat.orderRelatedUpdate(
520
556
  console.log('Related order updated:', updateResult.success);
521
557
  ```
522
558
 
559
+ ### Card Installments and Charging
560
+
561
+ ```typescript
562
+ const options = await tapsilat.getCardInstallments({
563
+ bin: '54066200',
564
+ amount: 100,
565
+ currency: 'TRY',
566
+ });
567
+ // options.installments lists each installment count with its commission rate and monthly payment
568
+
569
+ const table = await tapsilat.getInstallmentTable({ amount: 100, currency: 'TRY' });
570
+ // table.groups lists installment rows per bank card program
571
+
572
+ const charge = await tapsilat.chargeOrder({ order_reference_id: 'order-reference-id' });
573
+ if (!charge.isSuccess) {
574
+ // The request succeeded but the charge did not; inspect charge.errorCode and charge.message
575
+ }
576
+ ```
577
+
578
+ ### Submerchants, VPOS and Tokenization
579
+
580
+ Grouped services cover the remaining management APIs. Currency fields accept a currency UUID or an organization unit such as `TRY`; the server resolves units.
581
+
582
+ ```typescript
583
+ await tapsilat.submerchants.createSubmerchant({
584
+ locale: 'tr',
585
+ conversation_id: 'seller-1',
586
+ name: 'Example Ltd',
587
+ email: 'finance@example.test',
588
+ gsm_number: '905551112233',
589
+ sub_merchant_type: 'PRIVATE_COMPANY',
590
+ tax_number: '1234567890',
591
+ currency_id: 'TRY',
592
+ });
593
+
594
+ const posList = await tapsilat.vpos.listVpos({ page: 1, per_page: 20 });
595
+ const card = await tapsilat.tokenization.tokenizeCard({
596
+ card_number: '4111111111111111',
597
+ holder_name: 'John Doe',
598
+ expiry_month: '12',
599
+ expiry_year: '30',
600
+ cvv: '123',
601
+ user_id: 'customer-1',
602
+ });
603
+ ```
604
+
605
+ `updateVpos` and `updateSubmerchant` replace the stored record: read it first and send every field back. For example, leaving out `failover` or `tls` stores `false`.
606
+
607
+ ### Marketplace
608
+
609
+ `tapsilat.marketplace` uses the API version 2 routes, derived from your `baseURL`. Revisions are opaque strings: pass them back exactly as received.
610
+
611
+ ```typescript
612
+ const payout = await tapsilat.marketplace.getMarketplacePayout(payoutId);
613
+ if (payout.eligibility_status === 'eligible' && payout.release_status === 'awaiting_approval') {
614
+ await tapsilat.marketplace.approveMarketplacePayouts({ ids: [payout.id!] });
615
+ }
616
+
617
+ await tapsilat.marketplace.runMarketplacePayoutOperation(payoutId, {
618
+ kind: 'update_item',
619
+ idempotency_key: 'booking-123-allocation-change-1',
620
+ revision: payout.revision!,
621
+ seller_reference: seller.routing_reference,
622
+ net_amount: '60.25',
623
+ });
624
+ ```
625
+
626
+ Approval policies need a currency UUID; `createMarketplaceApprovalPolicy` and `updateMarketplaceApprovalPolicy` resolve units such as `TRY` for you, and `tapsilat.resolveCurrencyId('TRY')` exposes the same lookup.
627
+
628
+ ### Tapsilat Elements Sessions
629
+
630
+ Tapsilat Elements is the native Android and iOS card sheet. Your backend creates a session with its secret API key and passes only the session's `client_secret` to the app, which opens the sheet with it. Card data goes from the sheet to Tapsilat encrypted and never reaches your backend.
631
+
632
+ #### Create an Elements Session
633
+ ```typescript
634
+ import { ElementsSessionPurpose } from '@tapsilat/tapsilat-js';
635
+
636
+ const session = await tapsilat.createElementsSession({
637
+ purpose: ElementsSessionPurpose.PAY,
638
+ order_reference_id: order.reference_id,
639
+ save_card: true,
640
+ });
641
+
642
+ // Return session.id and session.client_secret to your app. Do not log or store the secret.
643
+ res.json({ id: session.id, client_secret: session.client_secret });
644
+ ```
645
+
646
+ A `pay` session needs `order_reference_id`. A `tokenize` session saves a card for `user_id`, with `mode` set to `ElementsTokenizeMode.SECURE` (3D Secure), `HALF_SECURE` or `NON_SECURE`:
647
+ ```typescript
648
+ import { ElementsSessionPurpose, ElementsTokenizeMode } from '@tapsilat/tapsilat-js';
649
+
650
+ const session = await tapsilat.createElementsSession({
651
+ purpose: ElementsSessionPurpose.TOKENIZE,
652
+ user_id: 'customer-1',
653
+ user_email: 'customer@example.test',
654
+ mode: ElementsTokenizeMode.SECURE,
655
+ });
656
+ ```
657
+
658
+ #### Get the Session Outcome
659
+ Read the outcome on your backend before fulfilling, instead of trusting what the app reports:
660
+ ```typescript
661
+ import { ElementsSessionStatus } from '@tapsilat/tapsilat-js';
662
+
663
+ const outcome = await tapsilat.getElementsSession(sessionId);
664
+ if (outcome.status === ElementsSessionStatus.SUCCEEDED) {
665
+ // fulfil the order; outcome.card holds the brand, last4 and expiry
666
+ } else if (outcome.status === ElementsSessionStatus.FAILED) {
667
+ // outcome.last_error?.code says why, for example "card_declined"
668
+ }
669
+ ```
670
+
671
+ #### Cancel an Elements Session
672
+ ```typescript
673
+ const canceled = await tapsilat.cancelElementsSession(sessionId);
674
+ ```
675
+ This cancels a session that is still waiting for a card. A session that is confirming or has completed comes back unchanged, so check `canceled.status`.
676
+
677
+ The client secret is returned only by `createElementsSession`. In Node.js, `console.log` and `util.inspect` print it as `[redacted]`, and `JSON.stringify` keeps it so you can still send it to the app; a copy made with the spread operator prints it in full. `status`, `purpose` and `card.brand` are plain strings, so compare them with `ElementsSessionStatus`, `ElementsSessionPurpose` and `ElementsCardBrand`. Elements errors carry a stable `apiCode` (for example `elements_not_enabled` with HTTP 403, `session_not_found` with HTTP 404) and `retryable`, which is `true` for a temporary failure such as HTTP 503 `unavailable`:
678
+ ```typescript
679
+ import { TapsilatError } from '@tapsilat/tapsilat-js';
680
+
681
+ try {
682
+ await tapsilat.getElementsSession(sessionId);
683
+ } catch (error) {
684
+ if (error instanceof TapsilatError && error.retryable) {
685
+ // try again later
686
+ }
687
+ }
688
+ ```
689
+
690
+ ### Error Details
691
+
692
+ Failed requests throw a `TapsilatError` (or a subclass). Besides `message` and `code`, it carries `statusCode`, the API's own `apiCode` and `apiStatus`, per-field validation `errors`, `retryable`, and the `rawBody`. Elements routes nest the error as `{"error": {"code": "...", "message": "...", "retryable": true}}`; `apiCode`, `message` and `retryable` are read from that object. Most "not found" errors arrive as HTTP 400, and rejected state changes (for example approving an ineligible payout) as HTTP 424. A successful status with an HTML body, which a deployment serving the panel UI returns for unknown routes, raises a `TapsilatError` with code `UNEXPECTED_RESPONSE`.
693
+
523
694
  ### Health Monitoring
524
695
 
525
696
  #### API Health Check