@paytree/medusa-payment-paytree 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.
Files changed (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +195 -0
  3. package/dist/index.d.ts +28 -0
  4. package/dist/index.js +55 -0
  5. package/dist/modules/paytree/index.d.ts +36 -0
  6. package/dist/modules/paytree/index.js +13 -0
  7. package/dist/modules/paytree/migrations/Migration20260617000000.d.ts +5 -0
  8. package/dist/modules/paytree/migrations/Migration20260617000000.js +54 -0
  9. package/dist/modules/paytree/migrations/Migration20260618000000.d.ts +5 -0
  10. package/dist/modules/paytree/migrations/Migration20260618000000.js +19 -0
  11. package/dist/modules/paytree/models/paytree-event-log.d.ts +25 -0
  12. package/dist/modules/paytree/models/paytree-event-log.js +29 -0
  13. package/dist/modules/paytree/models/paytree-setting.d.ts +23 -0
  14. package/dist/modules/paytree/models/paytree-setting.js +27 -0
  15. package/dist/modules/paytree/service.d.ts +60 -0
  16. package/dist/modules/paytree/service.js +47 -0
  17. package/dist/modules/paytree-payment/index.d.ts +8 -0
  18. package/dist/modules/paytree-payment/index.js +16 -0
  19. package/dist/modules/paytree-payment/lib/amount.d.ts +23 -0
  20. package/dist/modules/paytree-payment/lib/amount.js +40 -0
  21. package/dist/modules/paytree-payment/lib/audit.d.ts +26 -0
  22. package/dist/modules/paytree-payment/lib/audit.js +51 -0
  23. package/dist/modules/paytree-payment/lib/client.d.ts +28 -0
  24. package/dist/modules/paytree-payment/lib/client.js +81 -0
  25. package/dist/modules/paytree-payment/lib/constants.d.ts +54 -0
  26. package/dist/modules/paytree-payment/lib/constants.js +55 -0
  27. package/dist/modules/paytree-payment/lib/logger.d.ts +27 -0
  28. package/dist/modules/paytree-payment/lib/logger.js +73 -0
  29. package/dist/modules/paytree-payment/lib/methods.d.ts +69 -0
  30. package/dist/modules/paytree-payment/lib/methods.js +68 -0
  31. package/dist/modules/paytree-payment/lib/redact.d.ts +16 -0
  32. package/dist/modules/paytree-payment/lib/redact.js +60 -0
  33. package/dist/modules/paytree-payment/lib/settings-reader.d.ts +24 -0
  34. package/dist/modules/paytree-payment/lib/settings-reader.js +74 -0
  35. package/dist/modules/paytree-payment/lib/status-map.d.ts +26 -0
  36. package/dist/modules/paytree-payment/lib/status-map.js +66 -0
  37. package/dist/modules/paytree-payment/lib/types.d.ts +144 -0
  38. package/dist/modules/paytree-payment/lib/types.js +2 -0
  39. package/dist/modules/paytree-payment/service.d.ts +36 -0
  40. package/dist/modules/paytree-payment/service.js +369 -0
  41. package/docs/api-reference.md +121 -0
  42. package/docs/architecture.md +97 -0
  43. package/docs/configuration.md +62 -0
  44. package/docs/getting-started.md +111 -0
  45. package/docs/storefront-integration.md +85 -0
  46. package/integration/README.md +46 -0
  47. package/integration/admin/routes/paytree/api.ts +16 -0
  48. package/integration/admin/routes/paytree/method-icons.tsx +106 -0
  49. package/integration/admin/routes/paytree/page.tsx +18 -0
  50. package/integration/admin/routes/paytree/payments/page.tsx +240 -0
  51. package/integration/admin/routes/paytree/settings/page.tsx +214 -0
  52. package/integration/api/admin/paytree/events/route.ts +35 -0
  53. package/integration/api/admin/paytree/settings/route.ts +86 -0
  54. package/integration/api/hooks/paytree/route.ts +195 -0
  55. package/integration/api/store/paytree/methods/route.ts +28 -0
  56. package/package.json +57 -0
  57. package/storefront/README.md +25 -0
  58. package/storefront/components/payment-button.tsx +361 -0
  59. package/storefront/components/payment.tsx +356 -0
  60. package/storefront/components/paytree-method-icon.tsx +106 -0
  61. package/storefront/lib/paytree.ts +23 -0
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const utils_1 = require("@medusajs/framework/utils");
4
+ const paytree_setting_1 = require("./models/paytree-setting");
5
+ const paytree_event_log_1 = require("./models/paytree-event-log");
6
+ const SETTINGS_ID = "paytree_settings";
7
+ /**
8
+ * Service for the `paytree` module. Resolvable from app-level API routes and
9
+ * subscribers (not from the isolated payment provider — that path reads the
10
+ * table directly). Exposes settings get/upsert and audit-log queries used by
11
+ * the admin UI.
12
+ */
13
+ class PaytreeService extends (0, utils_1.MedusaService)({
14
+ PaytreeSetting: paytree_setting_1.PaytreeSetting,
15
+ PaytreeEventLog: paytree_event_log_1.PaytreeEventLog,
16
+ }) {
17
+ /** Return the single settings row, creating defaults if absent. */
18
+ async getSettings(defaults) {
19
+ const existing = await this.listPaytreeSettings({ id: SETTINGS_ID });
20
+ if (existing.length) {
21
+ return existing[0];
22
+ }
23
+ const [created] = await this.createPaytreeSettings([
24
+ {
25
+ id: SETTINGS_ID,
26
+ base_url: defaults.base_url,
27
+ log_request: defaults.log_request ?? false,
28
+ log_response: defaults.log_response ?? false,
29
+ methods: null,
30
+ },
31
+ ]);
32
+ return created;
33
+ }
34
+ /** Upsert the single settings row. */
35
+ async upsertSettings(values) {
36
+ // `methods` is a JSONB array; the generated model types it as an object, so
37
+ // we widen to `any` for the persistence call.
38
+ const payload = { id: SETTINGS_ID, ...values };
39
+ const existing = await this.listPaytreeSettings({ id: SETTINGS_ID });
40
+ if (existing.length) {
41
+ return this.updatePaytreeSettings(payload);
42
+ }
43
+ const [created] = await this.createPaytreeSettings([payload]);
44
+ return created;
45
+ }
46
+ }
47
+ exports.default = PaytreeService;
@@ -0,0 +1,8 @@
1
+ import PaytreePaymentProviderService from "./service";
2
+ /**
3
+ * Registers the Paytree payment provider with the Payment module.
4
+ * Reference this from medusa-config.ts under the payment module's `providers`.
5
+ */
6
+ declare const _default: import("@medusajs/types").ModuleProviderExports;
7
+ export default _default;
8
+ export { PaytreePaymentProviderService };
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PaytreePaymentProviderService = void 0;
7
+ const utils_1 = require("@medusajs/framework/utils");
8
+ const service_1 = __importDefault(require("./service"));
9
+ exports.PaytreePaymentProviderService = service_1.default;
10
+ /**
11
+ * Registers the Paytree payment provider with the Payment module.
12
+ * Reference this from medusa-config.ts under the payment module's `providers`.
13
+ */
14
+ exports.default = (0, utils_1.ModuleProvider)(utils_1.Modules.PAYMENT, {
15
+ services: [service_1.default],
16
+ });
@@ -0,0 +1,23 @@
1
+ import { BigNumber } from "@medusajs/framework/utils";
2
+ import type { BigNumberInput } from "@medusajs/framework/types";
3
+ /**
4
+ * Medusa v2 represents payment amounts in MAJOR units as BigNumber
5
+ * (e.g. 10.5 means 10.50 of the currency), unlike v1's minor units.
6
+ *
7
+ * Paytree's API accepts a decimal string limited to two decimal places
8
+ * (pattern: ^-?\d{0,8}(?:\.\d{0,2})?$). We therefore format to a fixed
9
+ * two-decimal string.
10
+ *
11
+ * Note: three-decimal currencies (BHD, KWD, OMR) are constrained by Paytree's
12
+ * own 2-decimal pattern, so amounts are rounded to 2 places to match the API.
13
+ */
14
+ export declare function toPaytreeAmount(amount: BigNumberInput): string;
15
+ /** Parse a Paytree decimal string back into a Medusa BigNumber. */
16
+ export declare function fromPaytreeAmount(amount: string): BigNumber;
17
+ /**
18
+ * Compute how much of a payment is newly refundable to record in Medusa,
19
+ * given the total refunded amount reported by Paytree and what Medusa has
20
+ * already recorded. Returns a non-negative decimal string, or null when
21
+ * there is nothing new to record.
22
+ */
23
+ export declare function newlyRefundedAmount(paytreeRefundedTotal: string, alreadyRecorded: BigNumberInput): string | null;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toPaytreeAmount = toPaytreeAmount;
4
+ exports.fromPaytreeAmount = fromPaytreeAmount;
5
+ exports.newlyRefundedAmount = newlyRefundedAmount;
6
+ const utils_1 = require("@medusajs/framework/utils");
7
+ /**
8
+ * Medusa v2 represents payment amounts in MAJOR units as BigNumber
9
+ * (e.g. 10.5 means 10.50 of the currency), unlike v1's minor units.
10
+ *
11
+ * Paytree's API accepts a decimal string limited to two decimal places
12
+ * (pattern: ^-?\d{0,8}(?:\.\d{0,2})?$). We therefore format to a fixed
13
+ * two-decimal string.
14
+ *
15
+ * Note: three-decimal currencies (BHD, KWD, OMR) are constrained by Paytree's
16
+ * own 2-decimal pattern, so amounts are rounded to 2 places to match the API.
17
+ */
18
+ function toPaytreeAmount(amount) {
19
+ const value = new utils_1.BigNumber(amount);
20
+ return value.numeric.toFixed(2);
21
+ }
22
+ /** Parse a Paytree decimal string back into a Medusa BigNumber. */
23
+ function fromPaytreeAmount(amount) {
24
+ return new utils_1.BigNumber(amount);
25
+ }
26
+ /**
27
+ * Compute how much of a payment is newly refundable to record in Medusa,
28
+ * given the total refunded amount reported by Paytree and what Medusa has
29
+ * already recorded. Returns a non-negative decimal string, or null when
30
+ * there is nothing new to record.
31
+ */
32
+ function newlyRefundedAmount(paytreeRefundedTotal, alreadyRecorded) {
33
+ const total = new utils_1.BigNumber(paytreeRefundedTotal);
34
+ const recorded = new utils_1.BigNumber(alreadyRecorded);
35
+ const delta = utils_1.MathBN.sub(total.numeric, recorded.numeric);
36
+ if (utils_1.MathBN.lte(delta, 0)) {
37
+ return null;
38
+ }
39
+ return new utils_1.BigNumber(delta).numeric.toFixed(2);
40
+ }
@@ -0,0 +1,26 @@
1
+ import { AuditEventType } from "./constants";
2
+ export interface AuditRecord {
3
+ event: AuditEventType;
4
+ /** Medusa payment session id (our transaction_ref). */
5
+ session_id?: string | null;
6
+ /** Paytree payment intent id. */
7
+ paytree_id?: string | null;
8
+ status?: string | null;
9
+ amount?: string | null;
10
+ currency?: string | null;
11
+ /** Free-form, already-safe summary. Redacted again defensively on write. */
12
+ detail?: Record<string, unknown> | null;
13
+ error?: string | null;
14
+ }
15
+ /**
16
+ * Writes to the shared `paytree_event_log` table via the container's Postgres
17
+ * connection. The `paytree` module owns the schema; the provider only inserts.
18
+ * Every payload is run through `redact` before it is written, so secrets and
19
+ * PII never land in the audit trail regardless of caller.
20
+ */
21
+ export declare class AuditWriter {
22
+ private readonly container_;
23
+ constructor(container: any);
24
+ private getConnection;
25
+ write(record: AuditRecord): Promise<void>;
26
+ }
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AuditWriter = void 0;
4
+ const utils_1 = require("@medusajs/framework/utils");
5
+ const crypto_1 = require("crypto");
6
+ const constants_1 = require("./constants");
7
+ const redact_1 = require("./redact");
8
+ /**
9
+ * Writes to the shared `paytree_event_log` table via the container's Postgres
10
+ * connection. The `paytree` module owns the schema; the provider only inserts.
11
+ * Every payload is run through `redact` before it is written, so secrets and
12
+ * PII never land in the audit trail regardless of caller.
13
+ */
14
+ class AuditWriter {
15
+ constructor(container) {
16
+ this.container_ = container;
17
+ }
18
+ getConnection() {
19
+ try {
20
+ return this.container_.resolve(utils_1.ContainerRegistrationKeys.PG_CONNECTION);
21
+ }
22
+ catch {
23
+ return this.container_?.[utils_1.ContainerRegistrationKeys.PG_CONNECTION] ?? null;
24
+ }
25
+ }
26
+ async write(record) {
27
+ const knex = this.getConnection();
28
+ if (!knex) {
29
+ return;
30
+ }
31
+ try {
32
+ await knex(constants_1.EVENT_LOG_TABLE).insert({
33
+ id: `ptlog_${(0, crypto_1.randomUUID)()}`,
34
+ event: record.event,
35
+ session_id: record.session_id ?? null,
36
+ paytree_id: record.paytree_id ?? null,
37
+ status: record.status ?? null,
38
+ amount: record.amount ?? null,
39
+ currency: record.currency ?? null,
40
+ detail: record.detail ? JSON.stringify((0, redact_1.redact)(record.detail)) : null,
41
+ error: record.error ?? null,
42
+ created_at: new Date(),
43
+ });
44
+ }
45
+ catch {
46
+ // Audit logging must never break a payment. Swallow write failures;
47
+ // the structured logger still captures the event in stdout.
48
+ }
49
+ }
50
+ }
51
+ exports.AuditWriter = AuditWriter;
@@ -0,0 +1,28 @@
1
+ import { PaytreeLogger, Correlation } from "./logger";
2
+ import { PaytreeCreateIntentBody, PaytreeCreateIntentResponse, PaytreePaymentIntent } from "./types";
3
+ export interface PaytreeClientConfig {
4
+ apiKey: string;
5
+ baseUrl: string;
6
+ logRequest: boolean;
7
+ logResponse: boolean;
8
+ }
9
+ /**
10
+ * Thin REST client over the Paytree API. One instance is created per call with
11
+ * the effective settings (so a base-URL change in the admin UI takes effect
12
+ * without a restart). The API key comes from env, never the DB.
13
+ */
14
+ export declare class PaytreeClient {
15
+ private readonly cfg_;
16
+ private readonly logger_;
17
+ constructor(cfg: PaytreeClientConfig, logger: PaytreeLogger);
18
+ private url;
19
+ private request;
20
+ createPaymentIntent(body: PaytreeCreateIntentBody, c?: Correlation): Promise<PaytreeCreateIntentResponse>;
21
+ getPaymentIntent(id: string, c?: Correlation): Promise<PaytreePaymentIntent>;
22
+ /**
23
+ * Look up a payment intent by our own transaction_ref. This is the reliable
24
+ * way to read status without depending on Paytree's id (which is an integer
25
+ * in the create response but a UUID on retrieve). Returns null if not found.
26
+ */
27
+ findByTransactionRef(transactionRef: string, c?: Correlation): Promise<PaytreePaymentIntent | null>;
28
+ }
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PaytreeClient = void 0;
4
+ const utils_1 = require("@medusajs/framework/utils");
5
+ /**
6
+ * Thin REST client over the Paytree API. One instance is created per call with
7
+ * the effective settings (so a base-URL change in the admin UI takes effect
8
+ * without a restart). The API key comes from env, never the DB.
9
+ */
10
+ class PaytreeClient {
11
+ constructor(cfg, logger) {
12
+ this.cfg_ = cfg;
13
+ this.logger_ = logger;
14
+ }
15
+ url(path) {
16
+ const base = this.cfg_.baseUrl.replace(/\/+$/, "");
17
+ return `${base}${path}`;
18
+ }
19
+ async request(method, path, body, c) {
20
+ const headers = {
21
+ Authorization: `Token ${this.cfg_.apiKey}`,
22
+ "Content-Type": "application/json",
23
+ };
24
+ this.logger_.request(this.cfg_.logRequest, method, path, headers, body, c);
25
+ let res;
26
+ try {
27
+ res = await fetch(this.url(path), {
28
+ method,
29
+ headers,
30
+ body: body ? JSON.stringify(body) : undefined,
31
+ });
32
+ }
33
+ catch (e) {
34
+ this.logger_.error(`network error calling ${method} ${path}`, e, c);
35
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, `Paytree request failed: ${e instanceof Error ? e.message : String(e)}`);
36
+ }
37
+ const text = await res.text();
38
+ let parsed = undefined;
39
+ if (text) {
40
+ try {
41
+ parsed = JSON.parse(text);
42
+ }
43
+ catch {
44
+ parsed = text;
45
+ }
46
+ }
47
+ this.logger_.response(this.cfg_.logResponse, method, path, res.status, parsed, c);
48
+ if (!res.ok) {
49
+ // Surface Paytree's response body in the error so the actual rejection
50
+ // reason (e.g. which field failed validation) is visible in the error
51
+ // logs, not only in the opt-in debug response line. The parsed body and
52
+ // status are also attached so callers can persist them to the audit log.
53
+ const detail = typeof parsed === "string" ? parsed : JSON.stringify(parsed ?? {});
54
+ const err = new utils_1.MedusaError(res.status === 401 || res.status === 403
55
+ ? utils_1.MedusaError.Types.NOT_ALLOWED
56
+ : utils_1.MedusaError.Types.UNEXPECTED_STATE, `Paytree ${method} ${path} returned ${res.status}: ${detail}`);
57
+ err.paytreeStatus = res.status;
58
+ err.paytreeResponse = parsed ?? null;
59
+ throw err;
60
+ }
61
+ return parsed;
62
+ }
63
+ createPaymentIntent(body, c) {
64
+ return this.request("POST", "/v1/transaction/payment_intent/", body, c);
65
+ }
66
+ getPaymentIntent(id, c) {
67
+ return this.request("GET", `/v1/transaction/payment_intent/${id}/`, undefined, c);
68
+ }
69
+ /**
70
+ * Look up a payment intent by our own transaction_ref. This is the reliable
71
+ * way to read status without depending on Paytree's id (which is an integer
72
+ * in the create response but a UUID on retrieve). Returns null if not found.
73
+ */
74
+ async findByTransactionRef(transactionRef, c) {
75
+ const query = encodeURIComponent(transactionRef);
76
+ const res = await this.request("GET", `/v1/transaction/payment_intent/?transaction_ref=${query}`, undefined, c);
77
+ const results = res?.results ?? [];
78
+ return results.find((r) => r.transaction_ref === transactionRef) ?? results[0] ?? null;
79
+ }
80
+ }
81
+ exports.PaytreeClient = PaytreeClient;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Shared constants for the Paytree integration.
3
+ *
4
+ * The provider identifier is what Medusa uses to build the provider id
5
+ * (`pp_<identifier>_<id>`) and the default webhook route. We use a custom
6
+ * GET callback route instead (see src/api/hooks/paytree), because Paytree
7
+ * sends a GET ping rather than a signed POST body.
8
+ */
9
+ /** Registration name of the regular module that owns the settings + audit-log tables. */
10
+ export declare const PAYTREE_MODULE = "paytree";
11
+ /** `static identifier` of the payment provider service. */
12
+ export declare const PAYTREE_PROVIDER_IDENTIFIER = "paytree";
13
+ /** Database tables (owned by the `paytree` module migration). */
14
+ export declare const SETTINGS_TABLE = "paytree_setting";
15
+ export declare const EVENT_LOG_TABLE = "paytree_event_log";
16
+ /** Single-row settings record id. We keep one global settings row. */
17
+ export declare const SETTINGS_SINGLETON_ID = "paytree_settings";
18
+ /** Default Paytree API origin. Overridable per-environment via the admin UI. */
19
+ export declare const DEFAULT_BASE_URL = "https://api.paytree.tech";
20
+ /** How long the provider caches settings read from the DB before re-reading. */
21
+ export declare const SETTINGS_CACHE_TTL_MS = 15000;
22
+ /**
23
+ * Paytree transaction statuses, as documented in the Paytree API.
24
+ * Kept as a const so the status map and audit log can reference them safely.
25
+ */
26
+ export declare const PaytreeStatus: {
27
+ readonly PENDING: "pending";
28
+ readonly PARTIAL: "partial";
29
+ readonly SUCCESS: "success";
30
+ readonly FAILED: "failed";
31
+ readonly CANCELED: "canceled";
32
+ readonly EXPIRED: "expired";
33
+ readonly AUTHORIZED: "authorized";
34
+ readonly REFUNDED: "refunded";
35
+ readonly PARTIAL_REFUNDED: "partial_refunded";
36
+ readonly DISPUTED: "disputed";
37
+ readonly DISPUTED_WON: "disputed_won";
38
+ readonly DISPUTED_LOST: "disputed_lost";
39
+ readonly DISPUTED_CLOSE: "disputed_close";
40
+ };
41
+ export type PaytreeStatusValue = (typeof PaytreeStatus)[keyof typeof PaytreeStatus];
42
+ /** Event types we persist to the audit log. */
43
+ export declare const AuditEvent: {
44
+ readonly INITIATE: "initiate_payment";
45
+ readonly AUTHORIZE: "authorize_payment";
46
+ readonly CAPTURE: "capture_payment";
47
+ readonly REFUND: "refund_payment";
48
+ readonly CANCEL: "cancel_payment";
49
+ readonly STATUS_READ: "get_payment_status";
50
+ readonly WEBHOOK_RECEIVED: "webhook_received";
51
+ readonly WEBHOOK_PROCESSED: "webhook_processed";
52
+ readonly ERROR: "error";
53
+ };
54
+ export type AuditEventType = (typeof AuditEvent)[keyof typeof AuditEvent];
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ /**
3
+ * Shared constants for the Paytree integration.
4
+ *
5
+ * The provider identifier is what Medusa uses to build the provider id
6
+ * (`pp_<identifier>_<id>`) and the default webhook route. We use a custom
7
+ * GET callback route instead (see src/api/hooks/paytree), because Paytree
8
+ * sends a GET ping rather than a signed POST body.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.AuditEvent = exports.PaytreeStatus = exports.SETTINGS_CACHE_TTL_MS = exports.DEFAULT_BASE_URL = exports.SETTINGS_SINGLETON_ID = exports.EVENT_LOG_TABLE = exports.SETTINGS_TABLE = exports.PAYTREE_PROVIDER_IDENTIFIER = exports.PAYTREE_MODULE = void 0;
12
+ /** Registration name of the regular module that owns the settings + audit-log tables. */
13
+ exports.PAYTREE_MODULE = "paytree";
14
+ /** `static identifier` of the payment provider service. */
15
+ exports.PAYTREE_PROVIDER_IDENTIFIER = "paytree";
16
+ /** Database tables (owned by the `paytree` module migration). */
17
+ exports.SETTINGS_TABLE = "paytree_setting";
18
+ exports.EVENT_LOG_TABLE = "paytree_event_log";
19
+ /** Single-row settings record id. We keep one global settings row. */
20
+ exports.SETTINGS_SINGLETON_ID = "paytree_settings";
21
+ /** Default Paytree API origin. Overridable per-environment via the admin UI. */
22
+ exports.DEFAULT_BASE_URL = "https://api.paytree.tech";
23
+ /** How long the provider caches settings read from the DB before re-reading. */
24
+ exports.SETTINGS_CACHE_TTL_MS = 15_000;
25
+ /**
26
+ * Paytree transaction statuses, as documented in the Paytree API.
27
+ * Kept as a const so the status map and audit log can reference them safely.
28
+ */
29
+ exports.PaytreeStatus = {
30
+ PENDING: "pending",
31
+ PARTIAL: "partial",
32
+ SUCCESS: "success",
33
+ FAILED: "failed",
34
+ CANCELED: "canceled",
35
+ EXPIRED: "expired",
36
+ AUTHORIZED: "authorized",
37
+ REFUNDED: "refunded",
38
+ PARTIAL_REFUNDED: "partial_refunded",
39
+ DISPUTED: "disputed",
40
+ DISPUTED_WON: "disputed_won",
41
+ DISPUTED_LOST: "disputed_lost",
42
+ DISPUTED_CLOSE: "disputed_close",
43
+ };
44
+ /** Event types we persist to the audit log. */
45
+ exports.AuditEvent = {
46
+ INITIATE: "initiate_payment",
47
+ AUTHORIZE: "authorize_payment",
48
+ CAPTURE: "capture_payment",
49
+ REFUND: "refund_payment",
50
+ CANCEL: "cancel_payment",
51
+ STATUS_READ: "get_payment_status",
52
+ WEBHOOK_RECEIVED: "webhook_received",
53
+ WEBHOOK_PROCESSED: "webhook_processed",
54
+ ERROR: "error",
55
+ };
@@ -0,0 +1,27 @@
1
+ export interface Correlation {
2
+ /** Medusa payment session id (our transaction_ref). */
3
+ sessionId?: string | null;
4
+ /** Paytree payment intent id. */
5
+ paytreeId?: string | null;
6
+ }
7
+ /**
8
+ * Wraps Medusa's logger. Every line carries the correlation ids so a single
9
+ * payment can be traced end to end (initiate -> redirect -> callback ->
10
+ * capture -> refund) by grepping the session or intent id.
11
+ *
12
+ * Request/response BODIES are only emitted when the corresponding admin toggle
13
+ * is on, and always after redaction. Operational lines (info/error) are always
14
+ * emitted.
15
+ */
16
+ export declare class PaytreeLogger {
17
+ private readonly logger_;
18
+ constructor(container: any);
19
+ private tag;
20
+ info(message: string, c?: Correlation): void;
21
+ warn(message: string, c?: Correlation): void;
22
+ error(message: string, error: unknown, c?: Correlation): void;
23
+ /** Log an outbound request body, redacted, only when enabled. */
24
+ request(enabled: boolean, method: string, path: string, headers: Record<string, string>, body: unknown, c?: Correlation): void;
25
+ /** Log a response body, redacted, only when enabled. */
26
+ response(enabled: boolean, method: string, path: string, httpStatus: number, body: unknown, c?: Correlation): void;
27
+ }
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PaytreeLogger = void 0;
4
+ const utils_1 = require("@medusajs/framework/utils");
5
+ const redact_1 = require("./redact");
6
+ const PREFIX = "[paytree]";
7
+ /**
8
+ * Wraps Medusa's logger. Every line carries the correlation ids so a single
9
+ * payment can be traced end to end (initiate -> redirect -> callback ->
10
+ * capture -> refund) by grepping the session or intent id.
11
+ *
12
+ * Request/response BODIES are only emitted when the corresponding admin toggle
13
+ * is on, and always after redaction. Operational lines (info/error) are always
14
+ * emitted.
15
+ */
16
+ class PaytreeLogger {
17
+ constructor(container) {
18
+ let resolved;
19
+ try {
20
+ resolved = container.resolve(utils_1.ContainerRegistrationKeys.LOGGER);
21
+ }
22
+ catch {
23
+ resolved = container?.logger;
24
+ }
25
+ this.logger_ =
26
+ resolved ??
27
+ {
28
+ info: console.log,
29
+ debug: console.debug,
30
+ warn: console.warn,
31
+ error: console.error,
32
+ };
33
+ }
34
+ tag(c) {
35
+ const parts = [];
36
+ if (c?.sessionId) {
37
+ parts.push(`session=${c.sessionId}`);
38
+ }
39
+ if (c?.paytreeId) {
40
+ parts.push(`intent=${c.paytreeId}`);
41
+ }
42
+ return parts.length ? ` (${parts.join(" ")})` : "";
43
+ }
44
+ info(message, c) {
45
+ this.logger_.info(`${PREFIX} ${message}${this.tag(c)}`);
46
+ }
47
+ warn(message, c) {
48
+ this.logger_.warn(`${PREFIX} ${message}${this.tag(c)}`);
49
+ }
50
+ error(message, error, c) {
51
+ const detail = error instanceof Error ? error.message : String(error);
52
+ this.logger_.error(`${PREFIX} ${message}: ${detail}${this.tag(c)}`);
53
+ }
54
+ /** Log an outbound request body, redacted, only when enabled. */
55
+ request(enabled, method, path, headers, body, c) {
56
+ if (!enabled) {
57
+ return;
58
+ }
59
+ const payload = JSON.stringify({
60
+ headers: (0, redact_1.redactHeaders)(headers),
61
+ body: (0, redact_1.redact)(body),
62
+ });
63
+ this.logger_.debug(`${PREFIX} -> ${method} ${path} ${payload}${this.tag(c)}`);
64
+ }
65
+ /** Log a response body, redacted, only when enabled. */
66
+ response(enabled, method, path, httpStatus, body, c) {
67
+ if (!enabled) {
68
+ return;
69
+ }
70
+ this.logger_.debug(`${PREFIX} <- ${method} ${path} ${httpStatus} ${JSON.stringify((0, redact_1.redact)(body))}${this.tag(c)}`);
71
+ }
72
+ }
73
+ exports.PaytreeLogger = PaytreeLogger;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Canonical list of payment methods Paytree supports, with their default
3
+ * display labels. The admin can, per method:
4
+ * - toggle whether it is offered at checkout (`enabled`)
5
+ * - override the label shown to the customer (`label`)
6
+ *
7
+ * The set of methods is fixed by Paytree; only `enabled` and `label` are
8
+ * stored in the DB. Resolving merges the stored config over these defaults so
9
+ * that adding a method here automatically surfaces it in the admin UI.
10
+ */
11
+ export declare const PAYTREE_METHOD_DEFS: readonly [{
12
+ readonly method: "card";
13
+ readonly label: "Credit / debit card";
14
+ }, {
15
+ readonly method: "paypal";
16
+ readonly label: "PayPal";
17
+ }, {
18
+ readonly method: "klarna";
19
+ readonly label: "Klarna";
20
+ }, {
21
+ readonly method: "ideal";
22
+ readonly label: "iDEAL";
23
+ }, {
24
+ readonly method: "bancontact";
25
+ readonly label: "Bancontact";
26
+ }, {
27
+ readonly method: "crypto";
28
+ readonly label: "Crypto";
29
+ }, {
30
+ readonly method: "wire";
31
+ readonly label: "Bank transfer";
32
+ }, {
33
+ readonly method: "trustly";
34
+ readonly label: "Trustly";
35
+ }, {
36
+ readonly method: "kakaopay";
37
+ readonly label: "KakaoPay";
38
+ }, {
39
+ readonly method: "p24";
40
+ readonly label: "Przelewy24";
41
+ }, {
42
+ readonly method: "blik";
43
+ readonly label: "BLIK";
44
+ }];
45
+ export type PaytreeMethodId = (typeof PAYTREE_METHOD_DEFS)[number]["method"];
46
+ /** What we persist per method (only the admin-editable bits). */
47
+ export interface StoredMethodConfig {
48
+ method: string;
49
+ enabled: boolean;
50
+ label?: string;
51
+ }
52
+ /** Fully resolved method config used by the admin UI and store endpoint. */
53
+ export interface ResolvedMethodConfig {
54
+ method: string;
55
+ enabled: boolean;
56
+ label: string;
57
+ }
58
+ /**
59
+ * Merge stored per-method config over the canonical defaults. Unknown stored
60
+ * methods are ignored; missing ones fall back to disabled with the default
61
+ * label. `card` is enabled by default so a fresh install has one working
62
+ * method until the admin curates the list.
63
+ */
64
+ export declare function resolveMethods(stored?: StoredMethodConfig[] | null): ResolvedMethodConfig[];
65
+ /**
66
+ * Normalize an incoming admin payload into the minimal stored shape, dropping
67
+ * unknown methods and blank label overrides.
68
+ */
69
+ export declare function normalizeMethods(input?: StoredMethodConfig[] | null): StoredMethodConfig[];