@parsrun/payments 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.
@@ -0,0 +1,1396 @@
1
+ // src/types.ts
2
+ import {
3
+ type,
4
+ currencyCode,
5
+ money,
6
+ paymentCustomer,
7
+ createCustomerRequest,
8
+ cardDetails,
9
+ paymentMethod,
10
+ paymentIntentStatus,
11
+ paymentIntent,
12
+ createPaymentIntentRequest,
13
+ subscriptionStatus,
14
+ priceInterval,
15
+ price,
16
+ subscription,
17
+ createSubscriptionRequest,
18
+ refundStatus,
19
+ refund,
20
+ createRefundRequest,
21
+ webhookEventType,
22
+ webhookEvent,
23
+ stripeConfig,
24
+ paddleConfig,
25
+ iyzicoConfig,
26
+ paymentsConfig
27
+ } from "@parsrun/types";
28
+ var PaymentError = class extends Error {
29
+ constructor(message, code, cause) {
30
+ super(message);
31
+ this.code = code;
32
+ this.cause = cause;
33
+ this.name = "PaymentError";
34
+ }
35
+ };
36
+ var PaymentErrorCodes = {
37
+ INVALID_CONFIG: "INVALID_CONFIG",
38
+ CUSTOMER_NOT_FOUND: "CUSTOMER_NOT_FOUND",
39
+ SUBSCRIPTION_NOT_FOUND: "SUBSCRIPTION_NOT_FOUND",
40
+ CHECKOUT_FAILED: "CHECKOUT_FAILED",
41
+ PAYMENT_FAILED: "PAYMENT_FAILED",
42
+ WEBHOOK_VERIFICATION_FAILED: "WEBHOOK_VERIFICATION_FAILED",
43
+ API_ERROR: "API_ERROR",
44
+ RATE_LIMITED: "RATE_LIMITED"
45
+ };
46
+
47
+ // src/providers/stripe.ts
48
+ var StripeProvider = class {
49
+ type = "stripe";
50
+ secretKey;
51
+ webhookSecret;
52
+ baseUrl = "https://api.stripe.com/v1";
53
+ apiVersion;
54
+ constructor(config) {
55
+ this.secretKey = config.secretKey;
56
+ this.webhookSecret = config.webhookSecret;
57
+ this.apiVersion = config.apiVersion ?? "2024-12-18.acacia";
58
+ }
59
+ async request(endpoint, options = {}) {
60
+ const { method = "GET", body } = options;
61
+ const headers = {
62
+ Authorization: `Bearer ${this.secretKey}`,
63
+ "Stripe-Version": this.apiVersion
64
+ };
65
+ const fetchOptions = {
66
+ method,
67
+ headers
68
+ };
69
+ if (body) {
70
+ headers["Content-Type"] = "application/x-www-form-urlencoded";
71
+ fetchOptions.body = this.encodeFormData(body);
72
+ }
73
+ const response = await fetch(`${this.baseUrl}${endpoint}`, fetchOptions);
74
+ const data = await response.json();
75
+ if (!response.ok || data.error) {
76
+ const errorMessage = data.error?.message ?? `HTTP ${response.status}`;
77
+ throw new PaymentError(
78
+ `Stripe API error: ${errorMessage}`,
79
+ data.error?.code ?? PaymentErrorCodes.API_ERROR,
80
+ data.error
81
+ );
82
+ }
83
+ return data;
84
+ }
85
+ encodeFormData(obj, prefix = "") {
86
+ const parts = [];
87
+ for (const [key, value] of Object.entries(obj)) {
88
+ if (value === void 0 || value === null) continue;
89
+ const fullKey = prefix ? `${prefix}[${key}]` : key;
90
+ if (typeof value === "object" && !Array.isArray(value)) {
91
+ parts.push(this.encodeFormData(value, fullKey));
92
+ } else if (Array.isArray(value)) {
93
+ value.forEach((item, index) => {
94
+ if (typeof item === "object") {
95
+ parts.push(this.encodeFormData(item, `${fullKey}[${index}]`));
96
+ } else {
97
+ parts.push(`${encodeURIComponent(`${fullKey}[${index}]`)}=${encodeURIComponent(String(item))}`);
98
+ }
99
+ });
100
+ } else {
101
+ parts.push(`${encodeURIComponent(fullKey)}=${encodeURIComponent(String(value))}`);
102
+ }
103
+ }
104
+ return parts.filter(Boolean).join("&");
105
+ }
106
+ // ============================================================================
107
+ // Customer
108
+ // ============================================================================
109
+ async createCustomer(options) {
110
+ const body = {
111
+ email: options.email
112
+ };
113
+ if (options.name) body["name"] = options.name;
114
+ if (options.phone) body["phone"] = options.phone;
115
+ if (options.metadata) body["metadata"] = options.metadata;
116
+ if (options.address) {
117
+ body["address"] = {
118
+ line1: options.address.line1,
119
+ line2: options.address.line2,
120
+ city: options.address.city,
121
+ state: options.address.state,
122
+ postal_code: options.address.postalCode,
123
+ country: options.address.country
124
+ };
125
+ }
126
+ const result = await this.request("/customers", {
127
+ method: "POST",
128
+ body
129
+ });
130
+ return this.mapCustomer(result);
131
+ }
132
+ async getCustomer(customerId) {
133
+ try {
134
+ const result = await this.request(`/customers/${customerId}`);
135
+ return this.mapCustomer(result);
136
+ } catch (err) {
137
+ if (err instanceof PaymentError && err.code === "resource_missing") {
138
+ return null;
139
+ }
140
+ throw err;
141
+ }
142
+ }
143
+ async updateCustomer(customerId, options) {
144
+ const body = {};
145
+ if (options.email) body["email"] = options.email;
146
+ if (options.name) body["name"] = options.name;
147
+ if (options.phone) body["phone"] = options.phone;
148
+ if (options.metadata) body["metadata"] = options.metadata;
149
+ if (options.address) {
150
+ body["address"] = {
151
+ line1: options.address.line1,
152
+ line2: options.address.line2,
153
+ city: options.address.city,
154
+ state: options.address.state,
155
+ postal_code: options.address.postalCode,
156
+ country: options.address.country
157
+ };
158
+ }
159
+ const result = await this.request(`/customers/${customerId}`, {
160
+ method: "POST",
161
+ body
162
+ });
163
+ return this.mapCustomer(result);
164
+ }
165
+ async deleteCustomer(customerId) {
166
+ await this.request(`/customers/${customerId}`, { method: "DELETE" });
167
+ }
168
+ mapCustomer(stripe) {
169
+ return {
170
+ id: stripe.id,
171
+ email: stripe.email ?? "",
172
+ name: stripe.name ?? void 0,
173
+ phone: stripe.phone ?? void 0,
174
+ address: stripe.address ? {
175
+ line1: stripe.address.line1 ?? void 0,
176
+ line2: stripe.address.line2 ?? void 0,
177
+ city: stripe.address.city ?? void 0,
178
+ state: stripe.address.state ?? void 0,
179
+ postalCode: stripe.address.postal_code ?? void 0,
180
+ country: stripe.address.country ?? void 0
181
+ } : void 0,
182
+ metadata: stripe.metadata ?? void 0,
183
+ providerData: stripe
184
+ };
185
+ }
186
+ // ============================================================================
187
+ // Checkout
188
+ // ============================================================================
189
+ async createCheckout(options) {
190
+ const body = {
191
+ mode: options.mode,
192
+ success_url: options.successUrl,
193
+ cancel_url: options.cancelUrl,
194
+ line_items: options.lineItems.map((item) => ({
195
+ price: item.priceId,
196
+ quantity: item.quantity
197
+ }))
198
+ };
199
+ if (options.customerId) body["customer"] = options.customerId;
200
+ if (options.customerEmail) body["customer_email"] = options.customerEmail;
201
+ if (options.allowPromotionCodes) body["allow_promotion_codes"] = true;
202
+ if (options.metadata) body["metadata"] = options.metadata;
203
+ if (options.mode === "subscription" && options.trialDays) {
204
+ body["subscription_data"] = {
205
+ trial_period_days: options.trialDays
206
+ };
207
+ }
208
+ const result = await this.request("/checkout/sessions", {
209
+ method: "POST",
210
+ body
211
+ });
212
+ return this.mapCheckoutSession(result);
213
+ }
214
+ async getCheckout(sessionId) {
215
+ try {
216
+ const result = await this.request(
217
+ `/checkout/sessions/${sessionId}`
218
+ );
219
+ return this.mapCheckoutSession(result);
220
+ } catch (err) {
221
+ if (err instanceof PaymentError && err.code === "resource_missing") {
222
+ return null;
223
+ }
224
+ throw err;
225
+ }
226
+ }
227
+ mapCheckoutSession(stripe) {
228
+ return {
229
+ id: stripe.id,
230
+ url: stripe.url ?? "",
231
+ customerId: stripe.customer ?? void 0,
232
+ status: stripe.status,
233
+ mode: stripe.mode,
234
+ amountTotal: stripe.amount_total ?? void 0,
235
+ currency: stripe.currency ?? void 0,
236
+ providerData: stripe
237
+ };
238
+ }
239
+ // ============================================================================
240
+ // Subscriptions
241
+ // ============================================================================
242
+ async createSubscription(options) {
243
+ const body = {
244
+ customer: options.customerId,
245
+ items: [{ price: options.priceId }]
246
+ };
247
+ if (options.trialDays) body["trial_period_days"] = options.trialDays;
248
+ if (options.metadata) body["metadata"] = options.metadata;
249
+ if (options.paymentBehavior) body["payment_behavior"] = options.paymentBehavior;
250
+ const result = await this.request("/subscriptions", {
251
+ method: "POST",
252
+ body
253
+ });
254
+ return this.mapSubscription(result);
255
+ }
256
+ async getSubscription(subscriptionId) {
257
+ try {
258
+ const result = await this.request(
259
+ `/subscriptions/${subscriptionId}`
260
+ );
261
+ return this.mapSubscription(result);
262
+ } catch (err) {
263
+ if (err instanceof PaymentError && err.code === "resource_missing") {
264
+ return null;
265
+ }
266
+ throw err;
267
+ }
268
+ }
269
+ async updateSubscription(subscriptionId, options) {
270
+ const body = {};
271
+ if (options.cancelAtPeriodEnd !== void 0) {
272
+ body["cancel_at_period_end"] = options.cancelAtPeriodEnd;
273
+ }
274
+ if (options.metadata) body["metadata"] = options.metadata;
275
+ if (options.prorationBehavior) body["proration_behavior"] = options.prorationBehavior;
276
+ if (options.priceId) {
277
+ const current = await this.request(
278
+ `/subscriptions/${subscriptionId}`
279
+ );
280
+ const itemId = current.items.data[0]?.id;
281
+ if (itemId) {
282
+ body["items"] = [{ id: itemId, price: options.priceId }];
283
+ }
284
+ }
285
+ const result = await this.request(
286
+ `/subscriptions/${subscriptionId}`,
287
+ { method: "POST", body }
288
+ );
289
+ return this.mapSubscription(result);
290
+ }
291
+ async cancelSubscription(subscriptionId, cancelAtPeriodEnd = true) {
292
+ if (cancelAtPeriodEnd) {
293
+ return this.updateSubscription(subscriptionId, { cancelAtPeriodEnd: true });
294
+ }
295
+ const result = await this.request(
296
+ `/subscriptions/${subscriptionId}`,
297
+ { method: "DELETE" }
298
+ );
299
+ return this.mapSubscription(result);
300
+ }
301
+ async listSubscriptions(customerId) {
302
+ const result = await this.request(
303
+ `/subscriptions?customer=${customerId}`
304
+ );
305
+ return result.data.map((sub) => this.mapSubscription(sub));
306
+ }
307
+ mapSubscription(stripe) {
308
+ const item = stripe.items.data[0];
309
+ return {
310
+ id: stripe.id,
311
+ customerId: typeof stripe.customer === "string" ? stripe.customer : stripe.customer.id,
312
+ status: stripe.status,
313
+ priceId: item?.price.id ?? "",
314
+ productId: typeof item?.price.product === "string" ? item.price.product : item?.price.product?.id,
315
+ currentPeriodStart: new Date(stripe.current_period_start * 1e3),
316
+ currentPeriodEnd: new Date(stripe.current_period_end * 1e3),
317
+ cancelAtPeriodEnd: stripe.cancel_at_period_end,
318
+ canceledAt: stripe.canceled_at ? new Date(stripe.canceled_at * 1e3) : void 0,
319
+ trialStart: stripe.trial_start ? new Date(stripe.trial_start * 1e3) : void 0,
320
+ trialEnd: stripe.trial_end ? new Date(stripe.trial_end * 1e3) : void 0,
321
+ metadata: stripe.metadata ?? void 0,
322
+ providerData: stripe
323
+ };
324
+ }
325
+ // ============================================================================
326
+ // Portal
327
+ // ============================================================================
328
+ async createPortalSession(options) {
329
+ const result = await this.request("/billing_portal/sessions", {
330
+ method: "POST",
331
+ body: {
332
+ customer: options.customerId,
333
+ return_url: options.returnUrl
334
+ }
335
+ });
336
+ return {
337
+ url: result.url,
338
+ returnUrl: options.returnUrl
339
+ };
340
+ }
341
+ // ============================================================================
342
+ // Products & Prices
343
+ // ============================================================================
344
+ async getProduct(productId) {
345
+ try {
346
+ const result = await this.request(`/products/${productId}`);
347
+ return this.mapProduct(result);
348
+ } catch (err) {
349
+ if (err instanceof PaymentError && err.code === "resource_missing") {
350
+ return null;
351
+ }
352
+ throw err;
353
+ }
354
+ }
355
+ async getPrice(priceId) {
356
+ try {
357
+ const result = await this.request(`/prices/${priceId}`);
358
+ return this.mapPrice(result);
359
+ } catch (err) {
360
+ if (err instanceof PaymentError && err.code === "resource_missing") {
361
+ return null;
362
+ }
363
+ throw err;
364
+ }
365
+ }
366
+ async listPrices(productId) {
367
+ let endpoint = "/prices?active=true&limit=100";
368
+ if (productId) {
369
+ endpoint += `&product=${productId}`;
370
+ }
371
+ const result = await this.request(endpoint);
372
+ return result.data.map((price2) => this.mapPrice(price2));
373
+ }
374
+ mapProduct(stripe) {
375
+ return {
376
+ id: stripe.id,
377
+ name: stripe.name,
378
+ description: stripe.description ?? void 0,
379
+ active: stripe.active,
380
+ metadata: stripe.metadata ?? void 0,
381
+ providerData: stripe
382
+ };
383
+ }
384
+ mapPrice(stripe) {
385
+ return {
386
+ id: stripe.id,
387
+ productId: typeof stripe.product === "string" ? stripe.product : stripe.product.id,
388
+ unitAmount: stripe.unit_amount ?? 0,
389
+ currency: stripe.currency.toUpperCase(),
390
+ recurring: stripe.recurring ? {
391
+ interval: stripe.recurring.interval,
392
+ intervalCount: stripe.recurring.interval_count
393
+ } : void 0,
394
+ active: stripe.active,
395
+ metadata: stripe.metadata ?? void 0,
396
+ providerData: stripe
397
+ };
398
+ }
399
+ // ============================================================================
400
+ // Webhooks
401
+ // ============================================================================
402
+ async verifyWebhook(payload, signature) {
403
+ if (!this.webhookSecret) {
404
+ throw new PaymentError(
405
+ "Webhook secret not configured",
406
+ PaymentErrorCodes.INVALID_CONFIG
407
+ );
408
+ }
409
+ const payloadString = typeof payload === "string" ? payload : new TextDecoder().decode(payload);
410
+ const signatureParts = signature.split(",").reduce((acc, part) => {
411
+ const [key, value] = part.split("=");
412
+ if (key && value) {
413
+ acc[key] = value;
414
+ }
415
+ return acc;
416
+ }, {});
417
+ const timestamp = signatureParts["t"];
418
+ const expectedSignature = signatureParts["v1"];
419
+ if (!timestamp || !expectedSignature) {
420
+ return null;
421
+ }
422
+ const timestampSeconds = parseInt(timestamp, 10);
423
+ const now = Math.floor(Date.now() / 1e3);
424
+ if (Math.abs(now - timestampSeconds) > 300) {
425
+ return null;
426
+ }
427
+ const signedPayload = `${timestamp}.${payloadString}`;
428
+ const computedSignature = await this.computeHmacSignature(
429
+ signedPayload,
430
+ this.webhookSecret
431
+ );
432
+ if (!this.secureCompare(computedSignature, expectedSignature)) {
433
+ return null;
434
+ }
435
+ const event = JSON.parse(payloadString);
436
+ return {
437
+ id: event.id,
438
+ type: this.mapEventType(event.type),
439
+ data: event.data.object,
440
+ created: new Date(event.created * 1e3),
441
+ provider: "stripe",
442
+ raw: event
443
+ };
444
+ }
445
+ async computeHmacSignature(payload, secret) {
446
+ const encoder = new TextEncoder();
447
+ const keyData = encoder.encode(secret);
448
+ const messageData = encoder.encode(payload);
449
+ const cryptoKey = await crypto.subtle.importKey(
450
+ "raw",
451
+ keyData,
452
+ { name: "HMAC", hash: "SHA-256" },
453
+ false,
454
+ ["sign"]
455
+ );
456
+ const signature = await crypto.subtle.sign("HMAC", cryptoKey, messageData);
457
+ const signatureArray = new Uint8Array(signature);
458
+ return Array.from(signatureArray).map((b) => b.toString(16).padStart(2, "0")).join("");
459
+ }
460
+ secureCompare(a, b) {
461
+ if (a.length !== b.length) return false;
462
+ let result = 0;
463
+ for (let i = 0; i < a.length; i++) {
464
+ result |= a.charCodeAt(i) ^ b.charCodeAt(i);
465
+ }
466
+ return result === 0;
467
+ }
468
+ mapEventType(stripeType) {
469
+ const mapping = {
470
+ "checkout.session.completed": "checkout.session.completed",
471
+ "checkout.session.expired": "checkout.session.expired",
472
+ "customer.created": "customer.created",
473
+ "customer.updated": "customer.updated",
474
+ "customer.deleted": "customer.deleted",
475
+ "customer.subscription.created": "subscription.created",
476
+ "customer.subscription.updated": "subscription.updated",
477
+ "customer.subscription.deleted": "subscription.deleted",
478
+ "customer.subscription.trial_will_end": "subscription.trial_will_end",
479
+ "payment_intent.succeeded": "payment.succeeded",
480
+ "payment_intent.payment_failed": "payment.failed",
481
+ "invoice.created": "invoice.created",
482
+ "invoice.paid": "invoice.paid",
483
+ "invoice.payment_failed": "invoice.payment_failed",
484
+ "invoice.upcoming": "invoice.upcoming",
485
+ "charge.refunded": "refund.created",
486
+ "refund.created": "refund.created",
487
+ "refund.updated": "refund.updated"
488
+ };
489
+ return mapping[stripeType] ?? "unknown";
490
+ }
491
+ // ============================================================================
492
+ // Usage Reporting (Metered Billing)
493
+ // ============================================================================
494
+ /**
495
+ * Report usage for metered billing
496
+ *
497
+ * @example
498
+ * ```typescript
499
+ * // Report 100 API calls for a subscription item
500
+ * await stripe.reportUsage({
501
+ * subscriptionItemId: "si_xxx",
502
+ * quantity: 100,
503
+ * action: "increment", // or "set" to replace
504
+ * });
505
+ * ```
506
+ */
507
+ async reportUsage(record) {
508
+ const body = {
509
+ quantity: record.quantity,
510
+ action: record.action ?? "increment"
511
+ };
512
+ if (record.timestamp) {
513
+ body["timestamp"] = Math.floor(record.timestamp.getTime() / 1e3);
514
+ }
515
+ const headers = {};
516
+ if (record.idempotencyKey) {
517
+ headers["Idempotency-Key"] = record.idempotencyKey;
518
+ }
519
+ await this.request(
520
+ `/subscription_items/${record.subscriptionItemId}/usage_records`,
521
+ {
522
+ method: "POST",
523
+ body
524
+ }
525
+ );
526
+ }
527
+ /**
528
+ * Report multiple usage records (batch)
529
+ * Note: Stripe doesn't have a batch API, so this is sequential
530
+ */
531
+ async reportUsageBatch(records) {
532
+ for (const record of records) {
533
+ await this.reportUsage(record);
534
+ }
535
+ }
536
+ /**
537
+ * Get subscription item ID for a subscription and price
538
+ */
539
+ async getSubscriptionItemId(subscriptionId, priceId) {
540
+ const subscription2 = await this.request(
541
+ `/subscriptions/${subscriptionId}`
542
+ );
543
+ const item = subscription2.items.data.find((i) => i.price.id === priceId);
544
+ return item?.id ?? null;
545
+ }
546
+ /**
547
+ * Get usage records for a subscription item
548
+ */
549
+ async getUsageRecords(subscriptionItemId, options) {
550
+ let endpoint = `/subscription_items/${subscriptionItemId}/usage_record_summaries?`;
551
+ if (options?.limit) {
552
+ endpoint += `limit=${options.limit}&`;
553
+ }
554
+ if (options?.startingAfter) {
555
+ endpoint += `starting_after=${options.startingAfter}&`;
556
+ }
557
+ if (options?.endingBefore) {
558
+ endpoint += `ending_before=${options.endingBefore}&`;
559
+ }
560
+ const result = await this.request(endpoint);
561
+ return {
562
+ data: result.data.map((r) => ({
563
+ id: r.id,
564
+ quantity: r.total_usage,
565
+ timestamp: new Date(r.period.start * 1e3),
566
+ subscriptionItem: r.subscription_item
567
+ })),
568
+ hasMore: result.has_more
569
+ };
570
+ }
571
+ /**
572
+ * Get current period usage total for a subscription item
573
+ */
574
+ async getCurrentUsage(subscriptionItemId) {
575
+ const result = await this.getUsageRecords(subscriptionItemId, { limit: 1 });
576
+ return result.data[0]?.quantity ?? 0;
577
+ }
578
+ };
579
+ function createStripeProvider(config) {
580
+ return new StripeProvider(config);
581
+ }
582
+
583
+ // src/providers/paddle.ts
584
+ var PaddleProvider = class {
585
+ type = "paddle";
586
+ apiKey;
587
+ webhookSecret;
588
+ baseUrl;
589
+ constructor(config) {
590
+ this.apiKey = config.apiKey;
591
+ this.webhookSecret = config.webhookSecret;
592
+ this.baseUrl = config.environment === "production" ? "https://api.paddle.com" : "https://sandbox-api.paddle.com";
593
+ }
594
+ async request(endpoint, options = {}) {
595
+ const { method = "GET", body } = options;
596
+ const headers = {
597
+ Authorization: `Bearer ${this.apiKey}`,
598
+ "Content-Type": "application/json"
599
+ };
600
+ const fetchOptions = {
601
+ method,
602
+ headers
603
+ };
604
+ if (body) {
605
+ fetchOptions.body = JSON.stringify(body);
606
+ }
607
+ const response = await fetch(`${this.baseUrl}${endpoint}`, fetchOptions);
608
+ const data = await response.json();
609
+ if (!response.ok || data.error) {
610
+ const errorMessage = data.error?.detail ?? `HTTP ${response.status}`;
611
+ throw new PaymentError(
612
+ `Paddle API error: ${errorMessage}`,
613
+ data.error?.code ?? PaymentErrorCodes.API_ERROR,
614
+ data.error
615
+ );
616
+ }
617
+ return data.data;
618
+ }
619
+ // ============================================================================
620
+ // Customer
621
+ // ============================================================================
622
+ async createCustomer(options) {
623
+ const body = {
624
+ email: options.email
625
+ };
626
+ if (options.name) body["name"] = options.name;
627
+ if (options.metadata) body["custom_data"] = options.metadata;
628
+ const result = await this.request("/customers", {
629
+ method: "POST",
630
+ body
631
+ });
632
+ return this.mapCustomer(result);
633
+ }
634
+ async getCustomer(customerId) {
635
+ try {
636
+ const result = await this.request(`/customers/${customerId}`);
637
+ return this.mapCustomer(result);
638
+ } catch (err) {
639
+ if (err instanceof PaymentError && err.code === "not_found") {
640
+ return null;
641
+ }
642
+ throw err;
643
+ }
644
+ }
645
+ async updateCustomer(customerId, options) {
646
+ const body = {};
647
+ if (options.email) body["email"] = options.email;
648
+ if (options.name) body["name"] = options.name;
649
+ if (options.metadata) body["custom_data"] = options.metadata;
650
+ const result = await this.request(`/customers/${customerId}`, {
651
+ method: "PATCH",
652
+ body
653
+ });
654
+ return this.mapCustomer(result);
655
+ }
656
+ async deleteCustomer(_customerId) {
657
+ throw new PaymentError(
658
+ "Paddle does not support customer deletion",
659
+ PaymentErrorCodes.API_ERROR
660
+ );
661
+ }
662
+ mapCustomer(paddle) {
663
+ return {
664
+ id: paddle.id,
665
+ email: paddle.email,
666
+ name: paddle.name ?? void 0,
667
+ metadata: paddle.custom_data ?? void 0,
668
+ providerData: paddle
669
+ };
670
+ }
671
+ // ============================================================================
672
+ // Checkout
673
+ // ============================================================================
674
+ async createCheckout(options) {
675
+ const items = options.lineItems.map((item) => ({
676
+ price_id: item.priceId,
677
+ quantity: item.quantity
678
+ }));
679
+ const body = {
680
+ items
681
+ };
682
+ if (options.customerId) body["customer_id"] = options.customerId;
683
+ if (options.customerEmail) {
684
+ body["customer"] = { email: options.customerEmail };
685
+ }
686
+ if (options.metadata) body["custom_data"] = options.metadata;
687
+ body["settings"] = {
688
+ success_url: options.successUrl
689
+ };
690
+ const result = await this.request("/transactions", {
691
+ method: "POST",
692
+ body
693
+ });
694
+ return {
695
+ id: result.id,
696
+ url: result.checkout?.url ?? "",
697
+ customerId: result.customer_id ?? void 0,
698
+ status: result.status === "completed" ? "complete" : "open",
699
+ mode: result.subscription_id ? "subscription" : "payment",
700
+ amountTotal: this.parsePaddleAmount(result.details?.totals?.total),
701
+ currency: result.currency_code,
702
+ providerData: result
703
+ };
704
+ }
705
+ async getCheckout(sessionId) {
706
+ try {
707
+ const result = await this.request(`/transactions/${sessionId}`);
708
+ return {
709
+ id: result.id,
710
+ url: result.checkout?.url ?? "",
711
+ customerId: result.customer_id ?? void 0,
712
+ status: result.status === "completed" ? "complete" : "open",
713
+ mode: result.subscription_id ? "subscription" : "payment",
714
+ amountTotal: this.parsePaddleAmount(result.details?.totals?.total),
715
+ currency: result.currency_code,
716
+ providerData: result
717
+ };
718
+ } catch (err) {
719
+ if (err instanceof PaymentError && err.code === "not_found") {
720
+ return null;
721
+ }
722
+ throw err;
723
+ }
724
+ }
725
+ // ============================================================================
726
+ // Subscriptions
727
+ // ============================================================================
728
+ async createSubscription(_options) {
729
+ throw new PaymentError(
730
+ "Paddle subscriptions must be created through checkout",
731
+ PaymentErrorCodes.API_ERROR
732
+ );
733
+ }
734
+ async getSubscription(subscriptionId) {
735
+ try {
736
+ const result = await this.request(
737
+ `/subscriptions/${subscriptionId}`
738
+ );
739
+ return this.mapSubscription(result);
740
+ } catch (err) {
741
+ if (err instanceof PaymentError && err.code === "not_found") {
742
+ return null;
743
+ }
744
+ throw err;
745
+ }
746
+ }
747
+ async updateSubscription(subscriptionId, options) {
748
+ const body = {};
749
+ if (options.priceId) {
750
+ body["items"] = [{ price_id: options.priceId, quantity: 1 }];
751
+ }
752
+ if (options.metadata) body["custom_data"] = options.metadata;
753
+ if (options.prorationBehavior) {
754
+ body["proration_billing_mode"] = options.prorationBehavior === "none" ? "do_not_bill" : "prorated_immediately";
755
+ }
756
+ const result = await this.request(
757
+ `/subscriptions/${subscriptionId}`,
758
+ { method: "PATCH", body }
759
+ );
760
+ return this.mapSubscription(result);
761
+ }
762
+ async cancelSubscription(subscriptionId, cancelAtPeriodEnd = true) {
763
+ const body = {
764
+ effective_from: cancelAtPeriodEnd ? "next_billing_period" : "immediately"
765
+ };
766
+ const result = await this.request(
767
+ `/subscriptions/${subscriptionId}/cancel`,
768
+ { method: "POST", body }
769
+ );
770
+ return this.mapSubscription(result);
771
+ }
772
+ async listSubscriptions(customerId) {
773
+ const result = await this.request(
774
+ `/subscriptions?customer_id=${customerId}`
775
+ );
776
+ return result.map((sub) => this.mapSubscription(sub));
777
+ }
778
+ mapSubscription(paddle) {
779
+ const item = paddle.items?.[0];
780
+ const statusMap = {
781
+ active: "active",
782
+ canceled: "canceled",
783
+ past_due: "past_due",
784
+ paused: "paused",
785
+ trialing: "trialing"
786
+ };
787
+ return {
788
+ id: paddle.id,
789
+ customerId: paddle.customer_id,
790
+ status: statusMap[paddle.status] ?? "active",
791
+ priceId: item?.price?.id ?? "",
792
+ productId: item?.price?.product_id,
793
+ currentPeriodStart: new Date(paddle.current_billing_period?.starts_at ?? Date.now()),
794
+ currentPeriodEnd: new Date(paddle.current_billing_period?.ends_at ?? Date.now()),
795
+ cancelAtPeriodEnd: paddle.scheduled_change?.action === "cancel",
796
+ canceledAt: paddle.canceled_at ? new Date(paddle.canceled_at) : void 0,
797
+ trialStart: paddle.started_at ? new Date(paddle.started_at) : void 0,
798
+ trialEnd: paddle.first_billed_at ? new Date(paddle.first_billed_at) : void 0,
799
+ metadata: paddle.custom_data ?? void 0,
800
+ providerData: paddle
801
+ };
802
+ }
803
+ // ============================================================================
804
+ // Portal
805
+ // ============================================================================
806
+ async createPortalSession(options) {
807
+ const customer = await this.getCustomer(options.customerId);
808
+ if (!customer) {
809
+ throw new PaymentError(
810
+ "Customer not found",
811
+ PaymentErrorCodes.CUSTOMER_NOT_FOUND
812
+ );
813
+ }
814
+ const result = await this.request(
815
+ `/customers/${options.customerId}/portal-sessions`,
816
+ { method: "POST" }
817
+ );
818
+ return {
819
+ url: result.urls.general.overview,
820
+ returnUrl: options.returnUrl
821
+ };
822
+ }
823
+ // ============================================================================
824
+ // Products & Prices
825
+ // ============================================================================
826
+ async getProduct(productId) {
827
+ try {
828
+ const result = await this.request(`/products/${productId}`);
829
+ return this.mapProduct(result);
830
+ } catch (err) {
831
+ if (err instanceof PaymentError && err.code === "not_found") {
832
+ return null;
833
+ }
834
+ throw err;
835
+ }
836
+ }
837
+ async getPrice(priceId) {
838
+ try {
839
+ const result = await this.request(`/prices/${priceId}`);
840
+ return this.mapPrice(result);
841
+ } catch (err) {
842
+ if (err instanceof PaymentError && err.code === "not_found") {
843
+ return null;
844
+ }
845
+ throw err;
846
+ }
847
+ }
848
+ async listPrices(productId) {
849
+ let endpoint = "/prices?status=active";
850
+ if (productId) {
851
+ endpoint += `&product_id=${productId}`;
852
+ }
853
+ const result = await this.request(endpoint);
854
+ return result.map((price2) => this.mapPrice(price2));
855
+ }
856
+ mapProduct(paddle) {
857
+ return {
858
+ id: paddle.id,
859
+ name: paddle.name,
860
+ description: paddle.description ?? void 0,
861
+ active: paddle.status === "active",
862
+ metadata: paddle.custom_data ?? void 0,
863
+ providerData: paddle
864
+ };
865
+ }
866
+ mapPrice(paddle) {
867
+ const amount = paddle.unit_price?.amount ? parseInt(paddle.unit_price.amount, 10) : 0;
868
+ return {
869
+ id: paddle.id,
870
+ productId: paddle.product_id,
871
+ unitAmount: amount,
872
+ currency: paddle.unit_price?.currency_code ?? "USD",
873
+ recurring: paddle.billing_cycle ? {
874
+ interval: paddle.billing_cycle.interval,
875
+ intervalCount: paddle.billing_cycle.frequency
876
+ } : void 0,
877
+ active: paddle.status === "active",
878
+ metadata: paddle.custom_data ?? void 0,
879
+ providerData: paddle
880
+ };
881
+ }
882
+ parsePaddleAmount(amount) {
883
+ if (!amount) return void 0;
884
+ return parseInt(amount, 10);
885
+ }
886
+ // ============================================================================
887
+ // Webhooks
888
+ // ============================================================================
889
+ async verifyWebhook(payload, signature) {
890
+ if (!this.webhookSecret) {
891
+ throw new PaymentError(
892
+ "Webhook secret not configured",
893
+ PaymentErrorCodes.INVALID_CONFIG
894
+ );
895
+ }
896
+ const payloadString = typeof payload === "string" ? payload : new TextDecoder().decode(payload);
897
+ const signatureParts = signature.split(";").reduce((acc, part) => {
898
+ const [key, value] = part.split("=");
899
+ if (key && value) {
900
+ acc[key] = value;
901
+ }
902
+ return acc;
903
+ }, {});
904
+ const timestamp = signatureParts["ts"];
905
+ const expectedSignature = signatureParts["h1"];
906
+ if (!timestamp || !expectedSignature) {
907
+ return null;
908
+ }
909
+ const signedPayload = `${timestamp}:${payloadString}`;
910
+ const computedSignature = await this.computeHmacSignature(
911
+ signedPayload,
912
+ this.webhookSecret
913
+ );
914
+ if (!this.secureCompare(computedSignature, expectedSignature)) {
915
+ return null;
916
+ }
917
+ const event = JSON.parse(payloadString);
918
+ return {
919
+ id: event.event_id,
920
+ type: this.mapEventType(event.event_type),
921
+ data: event.data,
922
+ created: new Date(event.occurred_at),
923
+ provider: "paddle",
924
+ raw: event
925
+ };
926
+ }
927
+ async computeHmacSignature(payload, secret) {
928
+ const encoder = new TextEncoder();
929
+ const keyData = encoder.encode(secret);
930
+ const messageData = encoder.encode(payload);
931
+ const cryptoKey = await crypto.subtle.importKey(
932
+ "raw",
933
+ keyData,
934
+ { name: "HMAC", hash: "SHA-256" },
935
+ false,
936
+ ["sign"]
937
+ );
938
+ const signature = await crypto.subtle.sign("HMAC", cryptoKey, messageData);
939
+ const signatureArray = new Uint8Array(signature);
940
+ return Array.from(signatureArray).map((b) => b.toString(16).padStart(2, "0")).join("");
941
+ }
942
+ secureCompare(a, b) {
943
+ if (a.length !== b.length) return false;
944
+ let result = 0;
945
+ for (let i = 0; i < a.length; i++) {
946
+ result |= a.charCodeAt(i) ^ b.charCodeAt(i);
947
+ }
948
+ return result === 0;
949
+ }
950
+ mapEventType(paddleType) {
951
+ const mapping = {
952
+ "transaction.completed": "checkout.session.completed",
953
+ "customer.created": "customer.created",
954
+ "customer.updated": "customer.updated",
955
+ "subscription.created": "subscription.created",
956
+ "subscription.updated": "subscription.updated",
957
+ "subscription.canceled": "subscription.deleted",
958
+ "subscription.past_due": "subscription.updated",
959
+ "subscription.activated": "subscription.created",
960
+ "transaction.payment_failed": "payment.failed",
961
+ "adjustment.created": "refund.created"
962
+ };
963
+ return mapping[paddleType] ?? "unknown";
964
+ }
965
+ };
966
+ function createPaddleProvider(config) {
967
+ return new PaddleProvider(config);
968
+ }
969
+
970
+ // src/providers/iyzico.ts
971
+ var IyzicoProvider = class {
972
+ type = "iyzico";
973
+ apiKey;
974
+ secretKey;
975
+ baseUrl;
976
+ constructor(config) {
977
+ this.apiKey = config.apiKey;
978
+ this.secretKey = config.secretKey;
979
+ this.baseUrl = config.baseUrl ?? (config.environment === "production" ? "https://api.iyzipay.com" : "https://sandbox-api.iyzipay.com");
980
+ }
981
+ async request(endpoint, body) {
982
+ const randomString = this.generateRandomString(8);
983
+ const authorizationString = await this.generateAuthorizationString(
984
+ body,
985
+ randomString
986
+ );
987
+ const response = await fetch(`${this.baseUrl}${endpoint}`, {
988
+ method: "POST",
989
+ headers: {
990
+ Accept: "application/json",
991
+ "Content-Type": "application/json",
992
+ Authorization: authorizationString,
993
+ "x-iyzi-rnd": randomString
994
+ },
995
+ body: JSON.stringify(body)
996
+ });
997
+ const data = await response.json();
998
+ if (data.status !== "success") {
999
+ throw new PaymentError(
1000
+ `iyzico API error: ${data.errorMessage ?? "Unknown error"}`,
1001
+ data.errorCode ?? PaymentErrorCodes.API_ERROR,
1002
+ data
1003
+ );
1004
+ }
1005
+ return data;
1006
+ }
1007
+ generateRandomString(length) {
1008
+ const chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1009
+ let result = "";
1010
+ const randomValues = new Uint8Array(length);
1011
+ crypto.getRandomValues(randomValues);
1012
+ for (let i = 0; i < length; i++) {
1013
+ const randomValue = randomValues[i];
1014
+ if (randomValue !== void 0) {
1015
+ result += chars[randomValue % chars.length];
1016
+ }
1017
+ }
1018
+ return result;
1019
+ }
1020
+ async generateAuthorizationString(body, randomString) {
1021
+ const pkiString = this.generatePkiString(body);
1022
+ const hashString = `${this.apiKey}${randomString}${this.secretKey}${pkiString}`;
1023
+ const encoder = new TextEncoder();
1024
+ const data = encoder.encode(hashString);
1025
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
1026
+ const hashArray = new Uint8Array(hashBuffer);
1027
+ const hashBase64 = btoa(String.fromCharCode(...hashArray));
1028
+ const authorizationString = `${this.apiKey}:${hashBase64}`;
1029
+ const authorizationBase64 = btoa(authorizationString);
1030
+ return `IYZWS ${authorizationBase64}`;
1031
+ }
1032
+ generatePkiString(obj) {
1033
+ const parts = [];
1034
+ for (const [key, value] of Object.entries(obj)) {
1035
+ if (value === void 0 || value === null) continue;
1036
+ if (Array.isArray(value)) {
1037
+ const arrayParts = value.map((item) => {
1038
+ if (typeof item === "object" && item !== null) {
1039
+ return this.generatePkiString(item);
1040
+ }
1041
+ return String(item);
1042
+ });
1043
+ parts.push(`${key}=[${arrayParts.join(", ")}]`);
1044
+ } else if (typeof value === "object") {
1045
+ parts.push(
1046
+ `${key}=[${this.generatePkiString(value)}]`
1047
+ );
1048
+ } else {
1049
+ parts.push(`${key}=${value}`);
1050
+ }
1051
+ }
1052
+ return `[${parts.join(",")}]`;
1053
+ }
1054
+ // ============================================================================
1055
+ // Customer - iyzico uses buyer info per transaction, not stored customers
1056
+ // ============================================================================
1057
+ async createCustomer(_options) {
1058
+ throw new PaymentError(
1059
+ "iyzico does not support stored customers. Use buyer info in checkout.",
1060
+ PaymentErrorCodes.API_ERROR
1061
+ );
1062
+ }
1063
+ async getCustomer(_customerId) {
1064
+ return null;
1065
+ }
1066
+ async updateCustomer(_customerId, _options) {
1067
+ throw new PaymentError(
1068
+ "iyzico does not support stored customers",
1069
+ PaymentErrorCodes.API_ERROR
1070
+ );
1071
+ }
1072
+ async deleteCustomer(_customerId) {
1073
+ throw new PaymentError(
1074
+ "iyzico does not support stored customers",
1075
+ PaymentErrorCodes.API_ERROR
1076
+ );
1077
+ }
1078
+ // ============================================================================
1079
+ // Checkout
1080
+ // ============================================================================
1081
+ async createCheckout(_options) {
1082
+ throw new PaymentError(
1083
+ "Use createCheckoutForm() with IyzicoCheckoutOptions for iyzico",
1084
+ PaymentErrorCodes.INVALID_CONFIG
1085
+ );
1086
+ }
1087
+ /**
1088
+ * Create iyzico checkout form (iframe/popup)
1089
+ */
1090
+ async createCheckoutForm(options) {
1091
+ const body = {
1092
+ locale: "tr",
1093
+ conversationId: options.conversationId ?? this.generateRandomString(16),
1094
+ price: options.price,
1095
+ paidPrice: options.paidPrice,
1096
+ currency: options.currency,
1097
+ basketId: options.metadata?.["basketId"] ?? this.generateRandomString(16),
1098
+ paymentGroup: "PRODUCT",
1099
+ callbackUrl: options.successUrl,
1100
+ buyer: {
1101
+ id: options.buyer.id,
1102
+ name: options.buyer.name,
1103
+ surname: options.buyer.surname,
1104
+ gsmNumber: options.buyer.gsmNumber,
1105
+ email: options.buyer.email,
1106
+ identityNumber: options.buyer.identityNumber,
1107
+ registrationAddress: options.buyer.registrationAddress,
1108
+ ip: options.buyer.ip,
1109
+ city: options.buyer.city,
1110
+ country: options.buyer.country
1111
+ },
1112
+ shippingAddress: {
1113
+ contactName: options.shippingAddress.contactName,
1114
+ city: options.shippingAddress.city,
1115
+ country: options.shippingAddress.country,
1116
+ address: options.shippingAddress.address
1117
+ },
1118
+ billingAddress: {
1119
+ contactName: options.billingAddress.contactName,
1120
+ city: options.billingAddress.city,
1121
+ country: options.billingAddress.country,
1122
+ address: options.billingAddress.address
1123
+ },
1124
+ basketItems: options.basketItems.map((item) => ({
1125
+ id: item.id,
1126
+ name: item.name,
1127
+ category1: item.category1,
1128
+ category2: item.category2,
1129
+ itemType: item.itemType,
1130
+ price: item.price
1131
+ }))
1132
+ };
1133
+ if (options.enabledInstallments) {
1134
+ body["enabledInstallments"] = options.enabledInstallments;
1135
+ }
1136
+ if (options.force3ds) {
1137
+ body["forceThreeDS"] = 1;
1138
+ }
1139
+ const result = await this.request(
1140
+ "/payment/iyzi-pos/checkoutform/initialize/auth/ecom",
1141
+ body
1142
+ );
1143
+ return {
1144
+ token: result.token,
1145
+ checkoutFormContent: result.checkoutFormContent,
1146
+ tokenExpireTime: result.tokenExpireTime,
1147
+ paymentPageUrl: result.paymentPageUrl
1148
+ };
1149
+ }
1150
+ /**
1151
+ * Retrieve checkout form result
1152
+ */
1153
+ async retrieveCheckoutForm(token) {
1154
+ const body = {
1155
+ locale: "tr",
1156
+ conversationId: this.generateRandomString(16),
1157
+ token
1158
+ };
1159
+ const result = await this.request(
1160
+ "/payment/iyzi-pos/checkoutform/auth/ecom/detail",
1161
+ body
1162
+ );
1163
+ return {
1164
+ paymentId: result.paymentId,
1165
+ status: result.status,
1166
+ paymentStatus: result.paymentStatus,
1167
+ price: result.price,
1168
+ paidPrice: result.paidPrice,
1169
+ currency: result.currency,
1170
+ installment: result.installment,
1171
+ basketId: result.basketId,
1172
+ binNumber: result.binNumber,
1173
+ lastFourDigits: result.lastFourDigits,
1174
+ cardAssociation: result.cardAssociation,
1175
+ cardFamily: result.cardFamily,
1176
+ cardType: result.cardType,
1177
+ fraudStatus: result.fraudStatus,
1178
+ raw: result
1179
+ };
1180
+ }
1181
+ async getCheckout(_sessionId) {
1182
+ return null;
1183
+ }
1184
+ // ============================================================================
1185
+ // 3D Secure Payment
1186
+ // ============================================================================
1187
+ /**
1188
+ * Initialize 3D Secure payment
1189
+ */
1190
+ async initialize3DSPayment(options) {
1191
+ const body = {
1192
+ locale: "tr",
1193
+ conversationId: options.conversationId ?? this.generateRandomString(16),
1194
+ price: options.price,
1195
+ paidPrice: options.paidPrice,
1196
+ currency: options.currency,
1197
+ installment: options.installment,
1198
+ basketId: this.generateRandomString(16),
1199
+ paymentChannel: "WEB",
1200
+ paymentGroup: "PRODUCT",
1201
+ paymentCard: options.paymentCard,
1202
+ buyer: options.buyer,
1203
+ shippingAddress: options.shippingAddress,
1204
+ billingAddress: options.billingAddress,
1205
+ basketItems: options.basketItems,
1206
+ callbackUrl: options.callbackUrl
1207
+ };
1208
+ const result = await this.request(
1209
+ "/payment/3dsecure/initialize",
1210
+ body
1211
+ );
1212
+ return {
1213
+ threeDSHtmlContent: result.threeDSHtmlContent,
1214
+ status: result.status
1215
+ };
1216
+ }
1217
+ /**
1218
+ * Complete 3D Secure payment after callback
1219
+ */
1220
+ async complete3DSPayment(paymentId, conversationId) {
1221
+ const body = {
1222
+ locale: "tr",
1223
+ conversationId: conversationId ?? this.generateRandomString(16),
1224
+ paymentId
1225
+ };
1226
+ const result = await this.request(
1227
+ "/payment/3dsecure/auth",
1228
+ body
1229
+ );
1230
+ return {
1231
+ paymentId: result.paymentId,
1232
+ status: result.status,
1233
+ paymentStatus: result.paymentStatus,
1234
+ price: result.price,
1235
+ paidPrice: result.paidPrice,
1236
+ currency: result.currency,
1237
+ installment: result.installment,
1238
+ basketId: result.basketId,
1239
+ binNumber: result.binNumber,
1240
+ lastFourDigits: result.lastFourDigits,
1241
+ cardAssociation: result.cardAssociation,
1242
+ cardFamily: result.cardFamily,
1243
+ cardType: result.cardType,
1244
+ fraudStatus: result.fraudStatus,
1245
+ raw: result
1246
+ };
1247
+ }
1248
+ // ============================================================================
1249
+ // Refund
1250
+ // ============================================================================
1251
+ /**
1252
+ * Create a refund
1253
+ */
1254
+ async createRefund(options) {
1255
+ const body = {
1256
+ locale: "tr",
1257
+ conversationId: options.conversationId ?? this.generateRandomString(16),
1258
+ paymentTransactionId: options.paymentTransactionId,
1259
+ price: options.price,
1260
+ currency: options.currency,
1261
+ ip: options.ip
1262
+ };
1263
+ const result = await this.request(
1264
+ "/payment/refund",
1265
+ body
1266
+ );
1267
+ return {
1268
+ paymentId: result.paymentId,
1269
+ paymentTransactionId: result.paymentTransactionId,
1270
+ price: result.price,
1271
+ status: result.status
1272
+ };
1273
+ }
1274
+ /**
1275
+ * Cancel a payment (full refund before settlement)
1276
+ */
1277
+ async cancelPayment(options) {
1278
+ const body = {
1279
+ locale: "tr",
1280
+ conversationId: options.conversationId ?? this.generateRandomString(16),
1281
+ paymentId: options.paymentId,
1282
+ ip: options.ip
1283
+ };
1284
+ const result = await this.request(
1285
+ "/payment/cancel",
1286
+ body
1287
+ );
1288
+ return {
1289
+ paymentId: result.paymentId,
1290
+ price: result.price,
1291
+ currency: result.currency,
1292
+ status: result.status
1293
+ };
1294
+ }
1295
+ // ============================================================================
1296
+ // Installment
1297
+ // ============================================================================
1298
+ /**
1299
+ * Get installment info for a BIN number
1300
+ */
1301
+ async getInstallmentInfo(binNumber, price2) {
1302
+ const body = {
1303
+ locale: "tr",
1304
+ conversationId: this.generateRandomString(16),
1305
+ binNumber: binNumber.substring(0, 6),
1306
+ price: price2
1307
+ };
1308
+ const result = await this.request(
1309
+ "/payment/iyzi-pos/installment",
1310
+ body
1311
+ );
1312
+ return {
1313
+ installmentDetails: result.installmentDetails ?? []
1314
+ };
1315
+ }
1316
+ // ============================================================================
1317
+ // Subscriptions - iyzico has separate subscription API
1318
+ // ============================================================================
1319
+ async createSubscription(_options) {
1320
+ throw new PaymentError(
1321
+ "Use iyzico subscription API methods directly",
1322
+ PaymentErrorCodes.API_ERROR
1323
+ );
1324
+ }
1325
+ async getSubscription(_subscriptionId) {
1326
+ return null;
1327
+ }
1328
+ async updateSubscription(_subscriptionId, _options) {
1329
+ throw new PaymentError(
1330
+ "Use iyzico subscription API methods directly",
1331
+ PaymentErrorCodes.API_ERROR
1332
+ );
1333
+ }
1334
+ async cancelSubscription(_subscriptionId, _cancelAtPeriodEnd) {
1335
+ throw new PaymentError(
1336
+ "Use iyzico subscription API methods directly",
1337
+ PaymentErrorCodes.API_ERROR
1338
+ );
1339
+ }
1340
+ async listSubscriptions(_customerId) {
1341
+ return [];
1342
+ }
1343
+ // ============================================================================
1344
+ // Portal - not supported
1345
+ // ============================================================================
1346
+ async createPortalSession(_options) {
1347
+ throw new PaymentError(
1348
+ "iyzico does not support customer portal",
1349
+ PaymentErrorCodes.API_ERROR
1350
+ );
1351
+ }
1352
+ // ============================================================================
1353
+ // Webhooks
1354
+ // ============================================================================
1355
+ async verifyWebhook(payload, _signature) {
1356
+ const payloadString = typeof payload === "string" ? payload : new TextDecoder().decode(payload);
1357
+ try {
1358
+ const data = JSON.parse(payloadString);
1359
+ if (data.token) {
1360
+ const result = await this.retrieveCheckoutForm(data.token);
1361
+ return {
1362
+ id: result.paymentId ?? data.token,
1363
+ type: this.mapEventType(data.status ?? result.status),
1364
+ data: result,
1365
+ created: /* @__PURE__ */ new Date(),
1366
+ provider: "iyzico",
1367
+ raw: data
1368
+ };
1369
+ }
1370
+ return null;
1371
+ } catch {
1372
+ return null;
1373
+ }
1374
+ }
1375
+ mapEventType(status) {
1376
+ const mapping = {
1377
+ success: "payment.succeeded",
1378
+ failure: "payment.failed",
1379
+ INIT_THREEDS: "payment.succeeded",
1380
+ CALLBACK_THREEDS: "payment.succeeded"
1381
+ };
1382
+ return mapping[status] ?? "payment.succeeded";
1383
+ }
1384
+ };
1385
+ function createIyzicoProvider(config) {
1386
+ return new IyzicoProvider(config);
1387
+ }
1388
+ export {
1389
+ IyzicoProvider,
1390
+ PaddleProvider,
1391
+ StripeProvider,
1392
+ createIyzicoProvider,
1393
+ createPaddleProvider,
1394
+ createStripeProvider
1395
+ };
1396
+ //# sourceMappingURL=index.js.map