@pandait.tech/payment-peigo 1.0.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.
package/dist/index.cjs ADDED
@@ -0,0 +1,98 @@
1
+ 'use strict';
2
+
3
+ // src/client.ts
4
+ function createPeigoClient(config) {
5
+ const base = config.baseUrl ?? "https://api.peigo.com.ec/v1";
6
+ const headers = {
7
+ "X-Api-Key": config.apiKey,
8
+ "X-Merchant-Id": config.merchantId,
9
+ "Content-Type": "application/json"
10
+ };
11
+ return {
12
+ async prepare(params) {
13
+ const res = await fetch(`${base}/payments/charge`, {
14
+ method: "POST",
15
+ headers,
16
+ body: JSON.stringify({
17
+ orderRef: params.orderRef,
18
+ amount: params.amount,
19
+ currency: "USD",
20
+ description: params.description ?? params.orderRef,
21
+ webhookUrl: config.webhookUrl,
22
+ returnUrl: config.returnUrl
23
+ })
24
+ });
25
+ if (!res.ok) {
26
+ const text = await res.text().catch(() => "");
27
+ throw new Error(`Peigo prepare ${res.status}: ${text}`);
28
+ }
29
+ return res.json();
30
+ },
31
+ async getStatus(orderRef) {
32
+ const url = `${base}/payments/status?orderRef=${encodeURIComponent(orderRef)}`;
33
+ const res = await fetch(url, { method: "GET", headers });
34
+ if (!res.ok) {
35
+ const text = await res.text().catch(() => "");
36
+ throw new Error(`Peigo getStatus ${res.status}: ${text}`);
37
+ }
38
+ return res.json();
39
+ }
40
+ };
41
+ }
42
+
43
+ // src/adapter.ts
44
+ async function runIdempotentWebhook(orderId, resolveStatus, paidStatus, processedRefs) {
45
+ if (!orderId) return { handled: false };
46
+ if (processedRefs.has(orderId)) return { handled: true, orderId };
47
+ const status = await resolveStatus();
48
+ if (status !== paidStatus) return { handled: false };
49
+ processedRefs.add(orderId);
50
+ return { handled: true, orderId };
51
+ }
52
+ var PAID_STATUS = "PAID";
53
+ function toConfirmResult(s) {
54
+ if (s.status === PAID_STATUS) return { status: "paid", transactionId: s.transactionId };
55
+ if (s.status === "FAILED" || s.status === "EXPIRED")
56
+ return { status: "failed", error: s.message ?? s.status };
57
+ return { status: "pending" };
58
+ }
59
+ function toPeigoPaymentMethod(client, opts) {
60
+ const processed = opts?.processedRefs ?? /* @__PURE__ */ new Set();
61
+ return {
62
+ id: "peigo",
63
+ label: "Peigo (wallet QR \xB7 Banco Guayaquil)",
64
+ async prepare(order) {
65
+ const result = await client.prepare({
66
+ orderRef: order.id,
67
+ amount: order.total,
68
+ description: `Orden ${order.id}`
69
+ });
70
+ return {
71
+ qrPayload: result.qrPayload,
72
+ paymentUrl: result.paymentUrl,
73
+ paymentCode: result.paymentCode,
74
+ transactionId: result.transactionId,
75
+ orderId: order.id
76
+ };
77
+ },
78
+ async confirm(ref) {
79
+ if (opts?.onConfirm) return opts.onConfirm(ref);
80
+ return toConfirmResult(await client.getStatus(ref));
81
+ },
82
+ async handleWebhook(payload) {
83
+ const p = payload;
84
+ const orderId = p?.orderRef;
85
+ return runIdempotentWebhook(
86
+ orderId,
87
+ async () => p?.status ?? (await client.getStatus(orderId)).status,
88
+ PAID_STATUS,
89
+ processed
90
+ );
91
+ }
92
+ };
93
+ }
94
+
95
+ exports.createPeigoClient = createPeigoClient;
96
+ exports.toPeigoPaymentMethod = toPeigoPaymentMethod;
97
+ //# sourceMappingURL=index.cjs.map
98
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client.ts","../src/adapter.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["// Peigo (Banco Guayaquil) — cliente HTTP mínimo\n// Wallet QR: genera QR dinámico o link de pago y consulta estado del cobro.\n\nexport interface PeigoConfig {\n /** API Key del comercio (PEIGO_API_KEY). */\n apiKey: string;\n /** ID del comercio (PEIGO_MERCHANT_ID). */\n merchantId: string;\n /** URL a la que Peigo notificará el resultado del pago (webhook). */\n webhookUrl: string;\n /** URL de retorno después del pago. */\n returnUrl: string;\n /** ponytail: base URL sobreescribible para tests/staging. */\n baseUrl?: string;\n}\n\nexport interface PeigoPrepareParams {\n /** Referencia única de la orden. */\n orderRef: string;\n /** Monto total en USD. */\n amount: number;\n /** Descripción del pedido (aparece en la pantalla del wallet). */\n description?: string;\n}\n\nexport interface PeigoPrepareResponse {\n /** Payload del QR dinámico (string para que la UI genere la imagen). */\n qrPayload?: string;\n /** Link de pago (alternativa al QR). */\n paymentUrl?: string;\n /** Código único de 6 dígitos. */\n paymentCode?: string;\n /** ID interno de la transacción en Peigo. */\n transactionId: string;\n}\n\nexport interface PeigoTransactionStatus {\n transactionId: string;\n orderRef: string;\n /** \"PAID\" | \"PENDING\" | \"FAILED\" | \"EXPIRED\" */\n status: string;\n authorizationCode?: string;\n message?: string;\n}\n\nexport interface PeigoClient {\n /** Genera un QR dinámico (y opcionalmente link/código) para cobrar la orden. */\n prepare(params: PeigoPrepareParams): Promise<PeigoPrepareResponse>;\n /** Consulta el estado actual de la transacción por orderRef. */\n getStatus(orderRef: string): Promise<PeigoTransactionStatus>;\n}\n\nexport function createPeigoClient(config: PeigoConfig): PeigoClient {\n const base = config.baseUrl ?? \"https://api.peigo.com.ec/v1\";\n const headers = {\n \"X-Api-Key\": config.apiKey,\n \"X-Merchant-Id\": config.merchantId,\n \"Content-Type\": \"application/json\",\n };\n\n return {\n async prepare(params) {\n const res = await fetch(`${base}/payments/charge`, {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n orderRef: params.orderRef,\n amount: params.amount,\n currency: \"USD\",\n description: params.description ?? params.orderRef,\n webhookUrl: config.webhookUrl,\n returnUrl: config.returnUrl,\n }),\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(`Peigo prepare ${res.status}: ${text}`);\n }\n return res.json() as Promise<PeigoPrepareResponse>;\n },\n\n async getStatus(orderRef) {\n const url = `${base}/payments/status?orderRef=${encodeURIComponent(orderRef)}`;\n const res = await fetch(url, { method: \"GET\", headers });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(`Peigo getStatus ${res.status}: ${text}`);\n }\n return res.json() as Promise<PeigoTransactionStatus>;\n },\n };\n}\n","// Tipos estructuralmente compatibles con payment-core (sin importarlo — evita dep de workspace en runtime).\nimport type { Order } from \"@pandait.tech/commerce-core\";\nimport type { PeigoClient, PeigoTransactionStatus } from \"./client.js\";\n\ninterface ConfirmResult {\n status: \"paid\" | \"pending\" | \"failed\" | \"challenge\";\n transactionId?: string;\n redirectUrl?: string;\n error?: string;\n}\ninterface WebhookResult {\n handled: boolean;\n orderId?: string;\n}\ninterface PaymentMethod {\n id: string;\n label: string;\n prepare(order: Order): Promise<Record<string, unknown>>;\n confirm(ref: string): Promise<ConfirmResult>;\n handleWebhook(payload: unknown): Promise<WebhookResult>;\n}\n\n// Inlinado de payment-core/wallet-qr — ponytail: elimina dep de workspace en runtime.\nasync function runIdempotentWebhook(\n orderId: string | undefined,\n resolveStatus: () => Promise<string>,\n paidStatus: string,\n processedRefs: Set<string>,\n): Promise<WebhookResult> {\n if (!orderId) return { handled: false };\n if (processedRefs.has(orderId)) return { handled: true, orderId };\n const status = await resolveStatus();\n if (status !== paidStatus) return { handled: false };\n processedRefs.add(orderId);\n return { handled: true, orderId };\n}\n\nconst PAID_STATUS = \"PAID\";\n\nfunction toConfirmResult(s: PeigoTransactionStatus): ConfirmResult {\n if (s.status === PAID_STATUS) return { status: \"paid\", transactionId: s.transactionId };\n if (s.status === \"FAILED\" || s.status === \"EXPIRED\")\n return { status: \"failed\", error: s.message ?? s.status };\n return { status: \"pending\" };\n}\n\nexport interface PeigoWebhookPayload {\n orderRef?: string;\n transactionId?: string;\n status?: string;\n authorizationCode?: string;\n}\n\nexport interface PeigoPaymentMethodOpts {\n /**\n * Set inyectable para idempotencia de webhooks.\n * ponytail: en memoria por defecto; reemplaza con persistencia en DB para multi-instancia.\n */\n processedRefs?: Set<string>;\n /** Override de confirm para entornos sin acceso directo a la API (tests, staging). */\n onConfirm?: (ref: string) => Promise<ConfirmResult>;\n}\n\nexport function toPeigoPaymentMethod(\n client: PeigoClient,\n opts?: PeigoPaymentMethodOpts,\n): PaymentMethod {\n // ponytail: in-memory; reemplazar con Set persistido en DB en prod multi-instancia\n const processed = opts?.processedRefs ?? new Set<string>();\n\n return {\n id: \"peigo\",\n label: \"Peigo (wallet QR · Banco Guayaquil)\",\n\n async prepare(order: Order): Promise<Record<string, unknown>> {\n const result = await client.prepare({\n orderRef: order.id,\n amount: order.total,\n description: `Orden ${order.id}`,\n });\n // Devuelve el payload/string del QR — la UI genera la imagen (ponytail: no generar aquí).\n return {\n qrPayload: result.qrPayload,\n paymentUrl: result.paymentUrl,\n paymentCode: result.paymentCode,\n transactionId: result.transactionId,\n orderId: order.id,\n };\n },\n\n async confirm(ref: string): Promise<ConfirmResult> {\n if (opts?.onConfirm) return opts.onConfirm(ref);\n return toConfirmResult(await client.getStatus(ref));\n },\n\n async handleWebhook(payload: unknown): Promise<WebhookResult> {\n const p = payload as PeigoWebhookPayload | null;\n const orderId = p?.orderRef;\n return runIdempotentWebhook(\n orderId,\n async () => p?.status ?? (await client.getStatus(orderId!)).status,\n PAID_STATUS,\n processed,\n );\n },\n };\n}\n"]}
@@ -0,0 +1,83 @@
1
+ import { Order } from '@pandait.tech/commerce-core';
2
+
3
+ interface PeigoConfig {
4
+ /** API Key del comercio (PEIGO_API_KEY). */
5
+ apiKey: string;
6
+ /** ID del comercio (PEIGO_MERCHANT_ID). */
7
+ merchantId: string;
8
+ /** URL a la que Peigo notificará el resultado del pago (webhook). */
9
+ webhookUrl: string;
10
+ /** URL de retorno después del pago. */
11
+ returnUrl: string;
12
+ /** ponytail: base URL sobreescribible para tests/staging. */
13
+ baseUrl?: string;
14
+ }
15
+ interface PeigoPrepareParams {
16
+ /** Referencia única de la orden. */
17
+ orderRef: string;
18
+ /** Monto total en USD. */
19
+ amount: number;
20
+ /** Descripción del pedido (aparece en la pantalla del wallet). */
21
+ description?: string;
22
+ }
23
+ interface PeigoPrepareResponse {
24
+ /** Payload del QR dinámico (string para que la UI genere la imagen). */
25
+ qrPayload?: string;
26
+ /** Link de pago (alternativa al QR). */
27
+ paymentUrl?: string;
28
+ /** Código único de 6 dígitos. */
29
+ paymentCode?: string;
30
+ /** ID interno de la transacción en Peigo. */
31
+ transactionId: string;
32
+ }
33
+ interface PeigoTransactionStatus {
34
+ transactionId: string;
35
+ orderRef: string;
36
+ /** "PAID" | "PENDING" | "FAILED" | "EXPIRED" */
37
+ status: string;
38
+ authorizationCode?: string;
39
+ message?: string;
40
+ }
41
+ interface PeigoClient {
42
+ /** Genera un QR dinámico (y opcionalmente link/código) para cobrar la orden. */
43
+ prepare(params: PeigoPrepareParams): Promise<PeigoPrepareResponse>;
44
+ /** Consulta el estado actual de la transacción por orderRef. */
45
+ getStatus(orderRef: string): Promise<PeigoTransactionStatus>;
46
+ }
47
+ declare function createPeigoClient(config: PeigoConfig): PeigoClient;
48
+
49
+ interface ConfirmResult {
50
+ status: "paid" | "pending" | "failed" | "challenge";
51
+ transactionId?: string;
52
+ redirectUrl?: string;
53
+ error?: string;
54
+ }
55
+ interface WebhookResult {
56
+ handled: boolean;
57
+ orderId?: string;
58
+ }
59
+ interface PaymentMethod {
60
+ id: string;
61
+ label: string;
62
+ prepare(order: Order): Promise<Record<string, unknown>>;
63
+ confirm(ref: string): Promise<ConfirmResult>;
64
+ handleWebhook(payload: unknown): Promise<WebhookResult>;
65
+ }
66
+ interface PeigoWebhookPayload {
67
+ orderRef?: string;
68
+ transactionId?: string;
69
+ status?: string;
70
+ authorizationCode?: string;
71
+ }
72
+ interface PeigoPaymentMethodOpts {
73
+ /**
74
+ * Set inyectable para idempotencia de webhooks.
75
+ * ponytail: en memoria por defecto; reemplaza con persistencia en DB para multi-instancia.
76
+ */
77
+ processedRefs?: Set<string>;
78
+ /** Override de confirm para entornos sin acceso directo a la API (tests, staging). */
79
+ onConfirm?: (ref: string) => Promise<ConfirmResult>;
80
+ }
81
+ declare function toPeigoPaymentMethod(client: PeigoClient, opts?: PeigoPaymentMethodOpts): PaymentMethod;
82
+
83
+ export { type PeigoClient, type PeigoConfig, type PeigoPaymentMethodOpts, type PeigoPrepareParams, type PeigoPrepareResponse, type PeigoTransactionStatus, type PeigoWebhookPayload, createPeigoClient, toPeigoPaymentMethod };
@@ -0,0 +1,83 @@
1
+ import { Order } from '@pandait.tech/commerce-core';
2
+
3
+ interface PeigoConfig {
4
+ /** API Key del comercio (PEIGO_API_KEY). */
5
+ apiKey: string;
6
+ /** ID del comercio (PEIGO_MERCHANT_ID). */
7
+ merchantId: string;
8
+ /** URL a la que Peigo notificará el resultado del pago (webhook). */
9
+ webhookUrl: string;
10
+ /** URL de retorno después del pago. */
11
+ returnUrl: string;
12
+ /** ponytail: base URL sobreescribible para tests/staging. */
13
+ baseUrl?: string;
14
+ }
15
+ interface PeigoPrepareParams {
16
+ /** Referencia única de la orden. */
17
+ orderRef: string;
18
+ /** Monto total en USD. */
19
+ amount: number;
20
+ /** Descripción del pedido (aparece en la pantalla del wallet). */
21
+ description?: string;
22
+ }
23
+ interface PeigoPrepareResponse {
24
+ /** Payload del QR dinámico (string para que la UI genere la imagen). */
25
+ qrPayload?: string;
26
+ /** Link de pago (alternativa al QR). */
27
+ paymentUrl?: string;
28
+ /** Código único de 6 dígitos. */
29
+ paymentCode?: string;
30
+ /** ID interno de la transacción en Peigo. */
31
+ transactionId: string;
32
+ }
33
+ interface PeigoTransactionStatus {
34
+ transactionId: string;
35
+ orderRef: string;
36
+ /** "PAID" | "PENDING" | "FAILED" | "EXPIRED" */
37
+ status: string;
38
+ authorizationCode?: string;
39
+ message?: string;
40
+ }
41
+ interface PeigoClient {
42
+ /** Genera un QR dinámico (y opcionalmente link/código) para cobrar la orden. */
43
+ prepare(params: PeigoPrepareParams): Promise<PeigoPrepareResponse>;
44
+ /** Consulta el estado actual de la transacción por orderRef. */
45
+ getStatus(orderRef: string): Promise<PeigoTransactionStatus>;
46
+ }
47
+ declare function createPeigoClient(config: PeigoConfig): PeigoClient;
48
+
49
+ interface ConfirmResult {
50
+ status: "paid" | "pending" | "failed" | "challenge";
51
+ transactionId?: string;
52
+ redirectUrl?: string;
53
+ error?: string;
54
+ }
55
+ interface WebhookResult {
56
+ handled: boolean;
57
+ orderId?: string;
58
+ }
59
+ interface PaymentMethod {
60
+ id: string;
61
+ label: string;
62
+ prepare(order: Order): Promise<Record<string, unknown>>;
63
+ confirm(ref: string): Promise<ConfirmResult>;
64
+ handleWebhook(payload: unknown): Promise<WebhookResult>;
65
+ }
66
+ interface PeigoWebhookPayload {
67
+ orderRef?: string;
68
+ transactionId?: string;
69
+ status?: string;
70
+ authorizationCode?: string;
71
+ }
72
+ interface PeigoPaymentMethodOpts {
73
+ /**
74
+ * Set inyectable para idempotencia de webhooks.
75
+ * ponytail: en memoria por defecto; reemplaza con persistencia en DB para multi-instancia.
76
+ */
77
+ processedRefs?: Set<string>;
78
+ /** Override de confirm para entornos sin acceso directo a la API (tests, staging). */
79
+ onConfirm?: (ref: string) => Promise<ConfirmResult>;
80
+ }
81
+ declare function toPeigoPaymentMethod(client: PeigoClient, opts?: PeigoPaymentMethodOpts): PaymentMethod;
82
+
83
+ export { type PeigoClient, type PeigoConfig, type PeigoPaymentMethodOpts, type PeigoPrepareParams, type PeigoPrepareResponse, type PeigoTransactionStatus, type PeigoWebhookPayload, createPeigoClient, toPeigoPaymentMethod };
package/dist/index.js ADDED
@@ -0,0 +1,95 @@
1
+ // src/client.ts
2
+ function createPeigoClient(config) {
3
+ const base = config.baseUrl ?? "https://api.peigo.com.ec/v1";
4
+ const headers = {
5
+ "X-Api-Key": config.apiKey,
6
+ "X-Merchant-Id": config.merchantId,
7
+ "Content-Type": "application/json"
8
+ };
9
+ return {
10
+ async prepare(params) {
11
+ const res = await fetch(`${base}/payments/charge`, {
12
+ method: "POST",
13
+ headers,
14
+ body: JSON.stringify({
15
+ orderRef: params.orderRef,
16
+ amount: params.amount,
17
+ currency: "USD",
18
+ description: params.description ?? params.orderRef,
19
+ webhookUrl: config.webhookUrl,
20
+ returnUrl: config.returnUrl
21
+ })
22
+ });
23
+ if (!res.ok) {
24
+ const text = await res.text().catch(() => "");
25
+ throw new Error(`Peigo prepare ${res.status}: ${text}`);
26
+ }
27
+ return res.json();
28
+ },
29
+ async getStatus(orderRef) {
30
+ const url = `${base}/payments/status?orderRef=${encodeURIComponent(orderRef)}`;
31
+ const res = await fetch(url, { method: "GET", headers });
32
+ if (!res.ok) {
33
+ const text = await res.text().catch(() => "");
34
+ throw new Error(`Peigo getStatus ${res.status}: ${text}`);
35
+ }
36
+ return res.json();
37
+ }
38
+ };
39
+ }
40
+
41
+ // src/adapter.ts
42
+ async function runIdempotentWebhook(orderId, resolveStatus, paidStatus, processedRefs) {
43
+ if (!orderId) return { handled: false };
44
+ if (processedRefs.has(orderId)) return { handled: true, orderId };
45
+ const status = await resolveStatus();
46
+ if (status !== paidStatus) return { handled: false };
47
+ processedRefs.add(orderId);
48
+ return { handled: true, orderId };
49
+ }
50
+ var PAID_STATUS = "PAID";
51
+ function toConfirmResult(s) {
52
+ if (s.status === PAID_STATUS) return { status: "paid", transactionId: s.transactionId };
53
+ if (s.status === "FAILED" || s.status === "EXPIRED")
54
+ return { status: "failed", error: s.message ?? s.status };
55
+ return { status: "pending" };
56
+ }
57
+ function toPeigoPaymentMethod(client, opts) {
58
+ const processed = opts?.processedRefs ?? /* @__PURE__ */ new Set();
59
+ return {
60
+ id: "peigo",
61
+ label: "Peigo (wallet QR \xB7 Banco Guayaquil)",
62
+ async prepare(order) {
63
+ const result = await client.prepare({
64
+ orderRef: order.id,
65
+ amount: order.total,
66
+ description: `Orden ${order.id}`
67
+ });
68
+ return {
69
+ qrPayload: result.qrPayload,
70
+ paymentUrl: result.paymentUrl,
71
+ paymentCode: result.paymentCode,
72
+ transactionId: result.transactionId,
73
+ orderId: order.id
74
+ };
75
+ },
76
+ async confirm(ref) {
77
+ if (opts?.onConfirm) return opts.onConfirm(ref);
78
+ return toConfirmResult(await client.getStatus(ref));
79
+ },
80
+ async handleWebhook(payload) {
81
+ const p = payload;
82
+ const orderId = p?.orderRef;
83
+ return runIdempotentWebhook(
84
+ orderId,
85
+ async () => p?.status ?? (await client.getStatus(orderId)).status,
86
+ PAID_STATUS,
87
+ processed
88
+ );
89
+ }
90
+ };
91
+ }
92
+
93
+ export { createPeigoClient, toPeigoPaymentMethod };
94
+ //# sourceMappingURL=index.js.map
95
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client.ts","../src/adapter.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// Peigo (Banco Guayaquil) — cliente HTTP mínimo\n// Wallet QR: genera QR dinámico o link de pago y consulta estado del cobro.\n\nexport interface PeigoConfig {\n /** API Key del comercio (PEIGO_API_KEY). */\n apiKey: string;\n /** ID del comercio (PEIGO_MERCHANT_ID). */\n merchantId: string;\n /** URL a la que Peigo notificará el resultado del pago (webhook). */\n webhookUrl: string;\n /** URL de retorno después del pago. */\n returnUrl: string;\n /** ponytail: base URL sobreescribible para tests/staging. */\n baseUrl?: string;\n}\n\nexport interface PeigoPrepareParams {\n /** Referencia única de la orden. */\n orderRef: string;\n /** Monto total en USD. */\n amount: number;\n /** Descripción del pedido (aparece en la pantalla del wallet). */\n description?: string;\n}\n\nexport interface PeigoPrepareResponse {\n /** Payload del QR dinámico (string para que la UI genere la imagen). */\n qrPayload?: string;\n /** Link de pago (alternativa al QR). */\n paymentUrl?: string;\n /** Código único de 6 dígitos. */\n paymentCode?: string;\n /** ID interno de la transacción en Peigo. */\n transactionId: string;\n}\n\nexport interface PeigoTransactionStatus {\n transactionId: string;\n orderRef: string;\n /** \"PAID\" | \"PENDING\" | \"FAILED\" | \"EXPIRED\" */\n status: string;\n authorizationCode?: string;\n message?: string;\n}\n\nexport interface PeigoClient {\n /** Genera un QR dinámico (y opcionalmente link/código) para cobrar la orden. */\n prepare(params: PeigoPrepareParams): Promise<PeigoPrepareResponse>;\n /** Consulta el estado actual de la transacción por orderRef. */\n getStatus(orderRef: string): Promise<PeigoTransactionStatus>;\n}\n\nexport function createPeigoClient(config: PeigoConfig): PeigoClient {\n const base = config.baseUrl ?? \"https://api.peigo.com.ec/v1\";\n const headers = {\n \"X-Api-Key\": config.apiKey,\n \"X-Merchant-Id\": config.merchantId,\n \"Content-Type\": \"application/json\",\n };\n\n return {\n async prepare(params) {\n const res = await fetch(`${base}/payments/charge`, {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n orderRef: params.orderRef,\n amount: params.amount,\n currency: \"USD\",\n description: params.description ?? params.orderRef,\n webhookUrl: config.webhookUrl,\n returnUrl: config.returnUrl,\n }),\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(`Peigo prepare ${res.status}: ${text}`);\n }\n return res.json() as Promise<PeigoPrepareResponse>;\n },\n\n async getStatus(orderRef) {\n const url = `${base}/payments/status?orderRef=${encodeURIComponent(orderRef)}`;\n const res = await fetch(url, { method: \"GET\", headers });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(`Peigo getStatus ${res.status}: ${text}`);\n }\n return res.json() as Promise<PeigoTransactionStatus>;\n },\n };\n}\n","// Tipos estructuralmente compatibles con payment-core (sin importarlo — evita dep de workspace en runtime).\nimport type { Order } from \"@pandait.tech/commerce-core\";\nimport type { PeigoClient, PeigoTransactionStatus } from \"./client.js\";\n\ninterface ConfirmResult {\n status: \"paid\" | \"pending\" | \"failed\" | \"challenge\";\n transactionId?: string;\n redirectUrl?: string;\n error?: string;\n}\ninterface WebhookResult {\n handled: boolean;\n orderId?: string;\n}\ninterface PaymentMethod {\n id: string;\n label: string;\n prepare(order: Order): Promise<Record<string, unknown>>;\n confirm(ref: string): Promise<ConfirmResult>;\n handleWebhook(payload: unknown): Promise<WebhookResult>;\n}\n\n// Inlinado de payment-core/wallet-qr — ponytail: elimina dep de workspace en runtime.\nasync function runIdempotentWebhook(\n orderId: string | undefined,\n resolveStatus: () => Promise<string>,\n paidStatus: string,\n processedRefs: Set<string>,\n): Promise<WebhookResult> {\n if (!orderId) return { handled: false };\n if (processedRefs.has(orderId)) return { handled: true, orderId };\n const status = await resolveStatus();\n if (status !== paidStatus) return { handled: false };\n processedRefs.add(orderId);\n return { handled: true, orderId };\n}\n\nconst PAID_STATUS = \"PAID\";\n\nfunction toConfirmResult(s: PeigoTransactionStatus): ConfirmResult {\n if (s.status === PAID_STATUS) return { status: \"paid\", transactionId: s.transactionId };\n if (s.status === \"FAILED\" || s.status === \"EXPIRED\")\n return { status: \"failed\", error: s.message ?? s.status };\n return { status: \"pending\" };\n}\n\nexport interface PeigoWebhookPayload {\n orderRef?: string;\n transactionId?: string;\n status?: string;\n authorizationCode?: string;\n}\n\nexport interface PeigoPaymentMethodOpts {\n /**\n * Set inyectable para idempotencia de webhooks.\n * ponytail: en memoria por defecto; reemplaza con persistencia en DB para multi-instancia.\n */\n processedRefs?: Set<string>;\n /** Override de confirm para entornos sin acceso directo a la API (tests, staging). */\n onConfirm?: (ref: string) => Promise<ConfirmResult>;\n}\n\nexport function toPeigoPaymentMethod(\n client: PeigoClient,\n opts?: PeigoPaymentMethodOpts,\n): PaymentMethod {\n // ponytail: in-memory; reemplazar con Set persistido en DB en prod multi-instancia\n const processed = opts?.processedRefs ?? new Set<string>();\n\n return {\n id: \"peigo\",\n label: \"Peigo (wallet QR · Banco Guayaquil)\",\n\n async prepare(order: Order): Promise<Record<string, unknown>> {\n const result = await client.prepare({\n orderRef: order.id,\n amount: order.total,\n description: `Orden ${order.id}`,\n });\n // Devuelve el payload/string del QR — la UI genera la imagen (ponytail: no generar aquí).\n return {\n qrPayload: result.qrPayload,\n paymentUrl: result.paymentUrl,\n paymentCode: result.paymentCode,\n transactionId: result.transactionId,\n orderId: order.id,\n };\n },\n\n async confirm(ref: string): Promise<ConfirmResult> {\n if (opts?.onConfirm) return opts.onConfirm(ref);\n return toConfirmResult(await client.getStatus(ref));\n },\n\n async handleWebhook(payload: unknown): Promise<WebhookResult> {\n const p = payload as PeigoWebhookPayload | null;\n const orderId = p?.orderRef;\n return runIdempotentWebhook(\n orderId,\n async () => p?.status ?? (await client.getStatus(orderId!)).status,\n PAID_STATUS,\n processed,\n );\n },\n };\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@pandait.tech/payment-peigo",
3
+ "version": "1.0.0",
4
+ "description": "Peigo (Banco Guayaquil) wallet QR payment adapter for panda-commerce-kit. Implements PaymentMethod via QR dinámico, link de pago y código único.",
5
+ "license": "MIT",
6
+ "private": false,
7
+ "engines": {
8
+ "node": ">=18"
9
+ },
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/PandaITorg/panda-commerce-kit.git",
16
+ "directory": "packages/payment-peigo"
17
+ },
18
+ "keywords": [
19
+ "peigo",
20
+ "banco-guayaquil",
21
+ "wallet",
22
+ "qr",
23
+ "payment-gateway",
24
+ "ecuador",
25
+ "panda-commerce-kit"
26
+ ],
27
+ "type": "module",
28
+ "main": "./dist/index.cjs",
29
+ "module": "./dist/index.js",
30
+ "types": "./dist/index.d.ts",
31
+ "exports": {
32
+ ".": {
33
+ "types": "./dist/index.d.ts",
34
+ "import": "./dist/index.js",
35
+ "require": "./dist/index.cjs"
36
+ }
37
+ },
38
+ "files": [
39
+ "dist"
40
+ ],
41
+ "dependencies": {},
42
+ "devDependencies": {
43
+ "@types/node": "^22.7.5",
44
+ "@vitest/coverage-v8": "^4.1.7",
45
+ "tsup": "^8.3.0",
46
+ "typescript": "^5.6.3",
47
+ "vitest": "^4.1.7",
48
+ "@pandait.tech/payment-core": "0.0.0",
49
+ "@pandait.tech/commerce-core": "0.0.0"
50
+ },
51
+ "scripts": {
52
+ "build": "tsup",
53
+ "dev": "tsup --watch",
54
+ "type-check": "tsc --noEmit",
55
+ "test": "vitest run --coverage",
56
+ "test:watch": "vitest",
57
+ "lint": "eslint src",
58
+ "clean": "rm -rf dist .turbo"
59
+ }
60
+ }