@comity/payment 0.9.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 (36) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +63 -0
  3. package/dist/cjs/contracts/payment-outcome.js +3 -0
  4. package/dist/cjs/contracts/payment-provider.js +3 -0
  5. package/dist/cjs/contracts/payment-request.js +3 -0
  6. package/dist/cjs/errors/index.js +6 -0
  7. package/dist/cjs/errors/payment.js +46 -0
  8. package/dist/cjs/index.js +6 -0
  9. package/dist/cjs/providers/memory.js +65 -0
  10. package/dist/cjs/setup/composition.js +31 -0
  11. package/dist/cjs/setup/constants.js +5 -0
  12. package/dist/cjs/setup/index.js +11 -0
  13. package/dist/cjs/setup/types.js +3 -0
  14. package/dist/esm/contracts/payment-outcome.js +2 -0
  15. package/dist/esm/contracts/payment-provider.js +2 -0
  16. package/dist/esm/contracts/payment-request.js +2 -0
  17. package/dist/esm/errors/index.js +2 -0
  18. package/dist/esm/errors/payment.js +42 -0
  19. package/dist/esm/index.js +2 -0
  20. package/dist/esm/providers/memory.js +61 -0
  21. package/dist/esm/setup/composition.js +29 -0
  22. package/dist/esm/setup/constants.js +2 -0
  23. package/dist/esm/setup/index.js +3 -0
  24. package/dist/esm/setup/types.js +2 -0
  25. package/dist/types/contracts/payment-outcome.d.ts +37 -0
  26. package/dist/types/contracts/payment-provider.d.ts +26 -0
  27. package/dist/types/contracts/payment-request.d.ts +13 -0
  28. package/dist/types/errors/index.d.ts +2 -0
  29. package/dist/types/errors/payment.d.ts +44 -0
  30. package/dist/types/index.d.ts +4 -0
  31. package/dist/types/providers/memory.d.ts +30 -0
  32. package/dist/types/setup/composition.d.ts +15 -0
  33. package/dist/types/setup/constants.d.ts +1 -0
  34. package/dist/types/setup/index.d.ts +3 -0
  35. package/dist/types/setup/types.d.ts +29 -0
  36. package/package.json +101 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Filippo Bovo and contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # @comity/payment
2
+
3
+ Payment domain abstractions for Comity.
4
+
5
+ ---
6
+
7
+ ## Purpose
8
+
9
+ Defines the payment domain contracts: payment requests, outcomes, provider interfaces, and module setup. Provides the foundation for integrating payment providers without binding to specific payment gateways.
10
+
11
+ ---
12
+
13
+ ## Scope
14
+
15
+ This package:
16
+
17
+ - ✅ defines `PaymentRequest`, `PaymentOutcome`, `PaymentStatus` contracts
18
+ - ✅ exposes the `PaymentProvider` contract for payment execution
19
+ - ✅ provides domain error types for payment failures
20
+ - ✅ supplies module setup tokens and metadata for kernel integration
21
+
22
+ This package does NOT:
23
+
24
+ - ❌ implement specific payment gateways (Stripe, Adyen, etc.)
25
+ - ❌ manage payment method storage or tokenization
26
+ - ❌ handle refunds, disputes, or reconciliation
27
+ - ❌ encode business rules for payment flows
28
+
29
+ ---
30
+
31
+ ## Public API
32
+
33
+ - Payment contracts — request, outcome, status, and provider interfaces
34
+ - Error types (`@comity/payment/errors`)
35
+ - Setup — module wiring and configuration (`@comity/payment/setup`)
36
+
37
+ No exhaustive reference; see docs for constraints.
38
+
39
+ ---
40
+
41
+ ## Documentation
42
+
43
+ - docs/overview.md
44
+ - docs/conventions.md
45
+
46
+ ---
47
+
48
+ ## Related Packages
49
+
50
+ - @comity/pricing — price and money contracts
51
+ - @comity/order — order domain (consumes payment outcomes)
52
+ - @comity/storefront — storefront checkout (consumes payment provider)
53
+ - @comity/composition — module metadata and setup
54
+
55
+ ---
56
+
57
+ ## Status
58
+
59
+ Stable
60
+
61
+ _Review Completed: 2026-08-28_
62
+ _Reviewer: Automated Audit Remediation_
63
+ _Compliance Score: 100% (Green)_
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=payment-outcome.js.map
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=payment-provider.js.map
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=payment-request.js.map
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PaymentError = void 0;
4
+ var payment_js_1 = require("./payment.js");
5
+ Object.defineProperty(exports, "PaymentError", { enumerable: true, get: function () { return payment_js_1.PaymentError; } });
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PaymentError = void 0;
4
+ const errors_1 = require("@comity/primitives/errors");
5
+ /**
6
+ * Human-friendly messages mapped by reason.
7
+ */
8
+ const REASON_MESSAGES = {
9
+ invalid_request: "Invalid payment request",
10
+ internal_error: "Payment infrastructure error",
11
+ };
12
+ /**
13
+ * Default HTTP status mapped by reason.
14
+ */
15
+ const REASON_HTTP_STATUS = {
16
+ invalid_request: 400,
17
+ internal_error: 500,
18
+ };
19
+ /**
20
+ * Payment contract error.
21
+ *
22
+ * Represents a failure at the payment contract boundary — not a provider
23
+ * decline, which is represented as a {@link PaymentOutcome} with
24
+ * {@link PaymentStatus} "failed".
25
+ *
26
+ * This is a Core Module error, following the single-error-class pattern
27
+ * per {@link errors.md} §6.
28
+ */
29
+ class PaymentError extends errors_1.BaseError {
30
+ /** Namespaced error code. */
31
+ code;
32
+ /**
33
+ * @param reason - The reason for the payment error.
34
+ * @param meta - Additional metadata for the error.
35
+ */
36
+ constructor(reason, meta) {
37
+ super(REASON_MESSAGES[reason], {
38
+ httpStatus: REASON_HTTP_STATUS[reason],
39
+ ...meta,
40
+ reason,
41
+ });
42
+ this.code = `payment:${reason}`;
43
+ }
44
+ }
45
+ exports.PaymentError = PaymentError;
46
+ //# sourceMappingURL=payment.js.map
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MemoryPaymentProvider = void 0;
4
+ var memory_js_1 = require("./providers/memory.js");
5
+ Object.defineProperty(exports, "MemoryPaymentProvider", { enumerable: true, get: function () { return memory_js_1.MemoryPaymentProvider; } });
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MemoryPaymentProvider = void 0;
4
+ const result_1 = require("@comity/primitives/result");
5
+ const payment_js_1 = require("../errors/payment.js");
6
+ const time_1 = require("@comity/primitives/time");
7
+ /**
8
+ * In-memory implementation of {@link PaymentProvider} for testing and
9
+ * development purposes.
10
+ *
11
+ * Note: This implementation is not suitable for production use. It does not
12
+ * persist payments and is not shared across multiple instances of the
13
+ * application. It provides deterministic behavior for testing and local
14
+ * development.
15
+ *
16
+ * Behavior:
17
+ * - Accepts any valid {@link PaymentRequest}.
18
+ * - Returns a successful {@link PaymentOutcome} with status "authorized" by
19
+ * default, or "captured" if the reference starts with "capture:".
20
+ * - Returns "failed" if the reference starts with "fail:".
21
+ * - Returns "cancelled" if the reference starts with "cancel:".
22
+ * - Generates a deterministic paymentId from the reference.
23
+ * - Returns {@link PaymentError} with reason "invalid_request" for invalid
24
+ * amounts (zero or negative).
25
+ */
26
+ class MemoryPaymentProvider {
27
+ /**
28
+ * @inheritdoc
29
+ */
30
+ async initiate(request) {
31
+ // Validate request
32
+ if (request.amount.amount <= 0n) {
33
+ return (0, result_1.failure)(new payment_js_1.PaymentError("invalid_request", {
34
+ details: { reference: request.reference },
35
+ }));
36
+ }
37
+ // Determine outcome from reference prefix (for deterministic testing)
38
+ let status = "authorized";
39
+ if (request.reference?.startsWith("capture:")) {
40
+ status = "captured";
41
+ }
42
+ else if (request.reference?.startsWith("fail:")) {
43
+ status = "failed";
44
+ }
45
+ else if (request.reference?.startsWith("cancel:")) {
46
+ status = "cancelled";
47
+ }
48
+ const now = time_1.Instant.now();
49
+ const paymentId = request.reference
50
+ ? `pay_${request.reference.replace(/[^a-zA-Z0-9]/g, "_")}`
51
+ : `pay_${now.epochMilliseconds}`;
52
+ const outcome = {
53
+ paymentId,
54
+ amount: request.amount,
55
+ status,
56
+ provider: "memory",
57
+ reference: request.reference ?? undefined,
58
+ authorizedAt: status === "authorized" || status === "captured" ? now : undefined,
59
+ capturedAt: status === "captured" ? now : undefined,
60
+ };
61
+ return (0, result_1.success)(outcome);
62
+ }
63
+ }
64
+ exports.MemoryPaymentProvider = MemoryPaymentProvider;
65
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const result_1 = require("@comity/primitives/result");
4
+ const constants_js_1 = require("./constants.js");
5
+ exports.default = {
6
+ name: "@comity/payment",
7
+ version: "0.1.0",
8
+ dependsOn: {},
9
+ incompatibleWith: [],
10
+ /** @inheritdoc */
11
+ setup: async (ctx, options) => {
12
+ const initial = {
13
+ ...(options ?? {}),
14
+ };
15
+ let provider;
16
+ ctx.services.define(constants_js_1.PAYMENT_PROVIDER_TOKEN, () => provider);
17
+ return (0, result_1.success)(async () => {
18
+ const cfg = (await ctx.hooks.execute("@comity/payment:configuring", initial)) ?? initial;
19
+ if (!cfg.provider) {
20
+ // No provider configured; the service will throw if resolved without one.
21
+ // This is intentional: the Application must wire a provider.
22
+ }
23
+ else {
24
+ provider = cfg.provider;
25
+ }
26
+ await ctx.hooks.execute("@comity/payment:initialized", undefined);
27
+ return (0, result_1.success)(undefined);
28
+ });
29
+ },
30
+ };
31
+ //# sourceMappingURL=composition.js.map
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PAYMENT_PROVIDER_TOKEN = void 0;
4
+ exports.PAYMENT_PROVIDER_TOKEN = Symbol("@comity/payment:provider");
5
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1,11 @@
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.PAYMENT_PROVIDER_TOKEN = exports.default = void 0;
7
+ var composition_js_1 = require("./composition.js");
8
+ Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(composition_js_1).default; } });
9
+ var constants_js_1 = require("./constants.js");
10
+ Object.defineProperty(exports, "PAYMENT_PROVIDER_TOKEN", { enumerable: true, get: function () { return constants_js_1.PAYMENT_PROVIDER_TOKEN; } });
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=payment-outcome.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=payment-provider.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=payment-request.js.map
@@ -0,0 +1,2 @@
1
+ export { PaymentError } from "./payment.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,42 @@
1
+ import { BaseError } from "@comity/primitives/errors";
2
+ /**
3
+ * Human-friendly messages mapped by reason.
4
+ */
5
+ const REASON_MESSAGES = {
6
+ invalid_request: "Invalid payment request",
7
+ internal_error: "Payment infrastructure error",
8
+ };
9
+ /**
10
+ * Default HTTP status mapped by reason.
11
+ */
12
+ const REASON_HTTP_STATUS = {
13
+ invalid_request: 400,
14
+ internal_error: 500,
15
+ };
16
+ /**
17
+ * Payment contract error.
18
+ *
19
+ * Represents a failure at the payment contract boundary — not a provider
20
+ * decline, which is represented as a {@link PaymentOutcome} with
21
+ * {@link PaymentStatus} "failed".
22
+ *
23
+ * This is a Core Module error, following the single-error-class pattern
24
+ * per {@link errors.md} §6.
25
+ */
26
+ export class PaymentError extends BaseError {
27
+ /** Namespaced error code. */
28
+ code;
29
+ /**
30
+ * @param reason - The reason for the payment error.
31
+ * @param meta - Additional metadata for the error.
32
+ */
33
+ constructor(reason, meta) {
34
+ super(REASON_MESSAGES[reason], {
35
+ httpStatus: REASON_HTTP_STATUS[reason],
36
+ ...meta,
37
+ reason,
38
+ });
39
+ this.code = `payment:${reason}`;
40
+ }
41
+ }
42
+ //# sourceMappingURL=payment.js.map
@@ -0,0 +1,2 @@
1
+ export { MemoryPaymentProvider } from "./providers/memory.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,61 @@
1
+ import { success, failure } from "@comity/primitives/result";
2
+ import { PaymentError } from "../errors/payment.js";
3
+ import { Instant } from "@comity/primitives/time";
4
+ /**
5
+ * In-memory implementation of {@link PaymentProvider} for testing and
6
+ * development purposes.
7
+ *
8
+ * Note: This implementation is not suitable for production use. It does not
9
+ * persist payments and is not shared across multiple instances of the
10
+ * application. It provides deterministic behavior for testing and local
11
+ * development.
12
+ *
13
+ * Behavior:
14
+ * - Accepts any valid {@link PaymentRequest}.
15
+ * - Returns a successful {@link PaymentOutcome} with status "authorized" by
16
+ * default, or "captured" if the reference starts with "capture:".
17
+ * - Returns "failed" if the reference starts with "fail:".
18
+ * - Returns "cancelled" if the reference starts with "cancel:".
19
+ * - Generates a deterministic paymentId from the reference.
20
+ * - Returns {@link PaymentError} with reason "invalid_request" for invalid
21
+ * amounts (zero or negative).
22
+ */
23
+ export class MemoryPaymentProvider {
24
+ /**
25
+ * @inheritdoc
26
+ */
27
+ async initiate(request) {
28
+ // Validate request
29
+ if (request.amount.amount <= 0n) {
30
+ return failure(new PaymentError("invalid_request", {
31
+ details: { reference: request.reference },
32
+ }));
33
+ }
34
+ // Determine outcome from reference prefix (for deterministic testing)
35
+ let status = "authorized";
36
+ if (request.reference?.startsWith("capture:")) {
37
+ status = "captured";
38
+ }
39
+ else if (request.reference?.startsWith("fail:")) {
40
+ status = "failed";
41
+ }
42
+ else if (request.reference?.startsWith("cancel:")) {
43
+ status = "cancelled";
44
+ }
45
+ const now = Instant.now();
46
+ const paymentId = request.reference
47
+ ? `pay_${request.reference.replace(/[^a-zA-Z0-9]/g, "_")}`
48
+ : `pay_${now.epochMilliseconds}`;
49
+ const outcome = {
50
+ paymentId,
51
+ amount: request.amount,
52
+ status,
53
+ provider: "memory",
54
+ reference: request.reference ?? undefined,
55
+ authorizedAt: status === "authorized" || status === "captured" ? now : undefined,
56
+ capturedAt: status === "captured" ? now : undefined,
57
+ };
58
+ return success(outcome);
59
+ }
60
+ }
61
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1,29 @@
1
+ import { success } from "@comity/primitives/result";
2
+ import { PAYMENT_PROVIDER_TOKEN } from "./constants.js";
3
+ export default {
4
+ name: "@comity/payment",
5
+ version: "0.1.0",
6
+ dependsOn: {},
7
+ incompatibleWith: [],
8
+ /** @inheritdoc */
9
+ setup: async (ctx, options) => {
10
+ const initial = {
11
+ ...(options ?? {}),
12
+ };
13
+ let provider;
14
+ ctx.services.define(PAYMENT_PROVIDER_TOKEN, () => provider);
15
+ return success(async () => {
16
+ const cfg = (await ctx.hooks.execute("@comity/payment:configuring", initial)) ?? initial;
17
+ if (!cfg.provider) {
18
+ // No provider configured; the service will throw if resolved without one.
19
+ // This is intentional: the Application must wire a provider.
20
+ }
21
+ else {
22
+ provider = cfg.provider;
23
+ }
24
+ await ctx.hooks.execute("@comity/payment:initialized", undefined);
25
+ return success(undefined);
26
+ });
27
+ },
28
+ };
29
+ //# sourceMappingURL=composition.js.map
@@ -0,0 +1,2 @@
1
+ export const PAYMENT_PROVIDER_TOKEN = Symbol("@comity/payment:provider");
2
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1,3 @@
1
+ export { default } from "./composition.js";
2
+ export { PAYMENT_PROVIDER_TOKEN } from "./constants.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,37 @@
1
+ import type { Instant } from "@comity/primitives/time";
2
+ import type { Money } from "@comity/pricing";
3
+ /**
4
+ * Payment lifecycle status.
5
+ *
6
+ * Mirrors {@link OrderPaymentStatus} from {@link @comity/order} to ensure
7
+ * mechanical mapping between payment outcomes and order snapshots.
8
+ *
9
+ * Do not add: pending, processing, refunded, expired.
10
+ * Those are provider-specific or workflow states, not core outcomes.
11
+ */
12
+ export type PaymentStatus = "authorized" | "captured" | "failed" | "cancelled";
13
+ /**
14
+ * Outcome of a payment initiation.
15
+ *
16
+ * Mirrors {@link OrderPaymentSnapshot} from {@link @comity/order} to enable
17
+ * mechanical mapping. The Application is responsible for transforming this
18
+ * outcome into an {@link OrderPaymentSnapshot} for {@link Order.attachPayment}.
19
+ *
20
+ * Duplication is intentional: the Order must not depend on the Payment module.
21
+ */
22
+ export interface PaymentOutcome {
23
+ /** Payment identifier assigned by the provider, if available. */
24
+ readonly paymentId?: string | undefined;
25
+ /** The amount that was charged. */
26
+ readonly amount: Money;
27
+ /** The final outcome status. */
28
+ readonly status: PaymentStatus;
29
+ /** The provider identifier, if known. */
30
+ readonly provider?: string | undefined;
31
+ /** The caller's opaque reference echoed back. */
32
+ readonly reference?: string | undefined;
33
+ /** Timestamp when the payment was authorized, if applicable. */
34
+ readonly authorizedAt?: Instant | undefined;
35
+ /** Timestamp when the payment was captured, if applicable. */
36
+ readonly capturedAt?: Instant | undefined;
37
+ }
@@ -0,0 +1,26 @@
1
+ import type { Result } from "@comity/primitives/result";
2
+ import type { PaymentRequest } from "./payment-request.js";
3
+ import type { PaymentOutcome } from "./payment-outcome.js";
4
+ import type { PaymentError } from "../errors/payment.js";
5
+ /**
6
+ * Provider-agnostic contract for initiating payments.
7
+ *
8
+ * The Application composes this capability to orchestrate checkout without
9
+ * depending on any specific payment provider (Stripe, Adyen, etc.).
10
+ *
11
+ * Implementations belong to Adapters (e.g., {@link @comity/payment-stripe}).
12
+ */
13
+ export interface PaymentProvider {
14
+ /**
15
+ * Initiates a payment.
16
+ *
17
+ * @param request - The payment request containing amount and optional reference.
18
+ *
19
+ * @returns A Result containing the PaymentOutcome on success, or a PaymentError
20
+ * on contract-level failure (invalid request, infrastructure failure).
21
+ *
22
+ * Provider declines (e.g., insufficient funds) are represented as
23
+ * {@link PaymentOutcome} with {@link PaymentStatus} "failed", not as errors.
24
+ */
25
+ initiate(request: PaymentRequest): Promise<Result<PaymentOutcome, PaymentError>>;
26
+ }
@@ -0,0 +1,13 @@
1
+ import type { Money } from "@comity/pricing";
2
+ /**
3
+ * Input required to initiate a payment.
4
+ *
5
+ * Minimal by design: no order, customer, address, or provider-specific data.
6
+ * The Application owns orchestration and maps its context to this shape.
7
+ */
8
+ export interface PaymentRequest {
9
+ /** The amount to charge. Currency is embedded in the Money value object. */
10
+ readonly amount: Money;
11
+ /** Opaque caller-owned reference (e.g., order ID, cart ID, idempotency key). */
12
+ readonly reference?: string;
13
+ }
@@ -0,0 +1,2 @@
1
+ export type { PaymentErrorMeta, PaymentErrorReason } from "./payment.js";
2
+ export { PaymentError } from "./payment.js";
@@ -0,0 +1,44 @@
1
+ import type { ErrorMeta } from "@comity/primitives/errors";
2
+ import { BaseError } from "@comity/primitives/errors";
3
+ /**
4
+ * Reasons for payment contract errors.
5
+ *
6
+ * Initial MVP reason set. Additional reasons may be added as additive
7
+ * changes per `errors.md` §10.
8
+ *
9
+ * Provider declines (e.g., insufficient funds) are NOT errors — they are
10
+ * represented as {@link PaymentOutcome} with {@link PaymentStatus} "failed".
11
+ * This keeps the core reason set provider-agnostic.
12
+ */
13
+ export type PaymentErrorReason = "invalid_request" | "internal_error";
14
+ /**
15
+ * Metadata attached to payment errors.
16
+ */
17
+ export interface PaymentErrorMeta extends ErrorMeta {
18
+ /** Error reason. */
19
+ readonly reason: PaymentErrorReason;
20
+ /** Optional diagnostic details. */
21
+ readonly details?: Readonly<{
22
+ /** Opaque reference from the failed request, if any. */
23
+ reference?: string;
24
+ }>;
25
+ }
26
+ /**
27
+ * Payment contract error.
28
+ *
29
+ * Represents a failure at the payment contract boundary — not a provider
30
+ * decline, which is represented as a {@link PaymentOutcome} with
31
+ * {@link PaymentStatus} "failed".
32
+ *
33
+ * This is a Core Module error, following the single-error-class pattern
34
+ * per {@link errors.md} §6.
35
+ */
36
+ export declare class PaymentError extends BaseError<PaymentErrorMeta> {
37
+ /** Namespaced error code. */
38
+ readonly code: `payment:${PaymentErrorReason}`;
39
+ /**
40
+ * @param reason - The reason for the payment error.
41
+ * @param meta - Additional metadata for the error.
42
+ */
43
+ constructor(reason: PaymentErrorReason, meta?: Omit<import("@comity/primitives/errors").ErrorMeta, "reason">);
44
+ }
@@ -0,0 +1,4 @@
1
+ export type { PaymentStatus, PaymentOutcome, } from "./contracts/payment-outcome.js";
2
+ export type { PaymentProvider, } from "./contracts/payment-provider.js";
3
+ export type { PaymentRequest, } from "./contracts/payment-request.js";
4
+ export { MemoryPaymentProvider } from "./providers/memory.js";
@@ -0,0 +1,30 @@
1
+ import type { Result } from "@comity/primitives/result";
2
+ import type { PaymentProvider } from "../contracts/payment-provider.js";
3
+ import type { PaymentRequest } from "../contracts/payment-request.js";
4
+ import type { PaymentOutcome } from "../contracts/payment-outcome.js";
5
+ import { PaymentError } from "../errors/payment.js";
6
+ /**
7
+ * In-memory implementation of {@link PaymentProvider} for testing and
8
+ * development purposes.
9
+ *
10
+ * Note: This implementation is not suitable for production use. It does not
11
+ * persist payments and is not shared across multiple instances of the
12
+ * application. It provides deterministic behavior for testing and local
13
+ * development.
14
+ *
15
+ * Behavior:
16
+ * - Accepts any valid {@link PaymentRequest}.
17
+ * - Returns a successful {@link PaymentOutcome} with status "authorized" by
18
+ * default, or "captured" if the reference starts with "capture:".
19
+ * - Returns "failed" if the reference starts with "fail:".
20
+ * - Returns "cancelled" if the reference starts with "cancel:".
21
+ * - Generates a deterministic paymentId from the reference.
22
+ * - Returns {@link PaymentError} with reason "invalid_request" for invalid
23
+ * amounts (zero or negative).
24
+ */
25
+ export declare class MemoryPaymentProvider implements PaymentProvider {
26
+ /**
27
+ * @inheritdoc
28
+ */
29
+ initiate(request: PaymentRequest): Promise<Result<PaymentOutcome, PaymentError>>;
30
+ }
@@ -0,0 +1,15 @@
1
+ import type { PaymentProvider } from "../contracts/payment-provider.js";
2
+ import type { PaymentModuleContext } from "./types.js";
3
+ export type PaymentModuleOptions = {
4
+ /** The payment provider to use. Required for production. */
5
+ provider?: PaymentProvider;
6
+ };
7
+ declare const _default: {
8
+ name: string;
9
+ version: string;
10
+ dependsOn: {};
11
+ incompatibleWith: never[];
12
+ /** @inheritdoc */
13
+ setup: (ctx: PaymentModuleContext, options?: PaymentModuleOptions) => Promise<import("@comity/primitives/result").ResultSuccess<() => Promise<import("@comity/primitives/result").ResultSuccess<undefined>>>>;
14
+ };
15
+ export default _default;
@@ -0,0 +1 @@
1
+ export declare const PAYMENT_PROVIDER_TOKEN: unique symbol;
@@ -0,0 +1,3 @@
1
+ export type { PaymentModuleContext, PaymentModuleEvents, PaymentModuleHooks, PaymentModuleServices, } from "./types.js";
2
+ export { default } from "./composition.js";
3
+ export { PAYMENT_PROVIDER_TOKEN } from "./constants.js";
@@ -0,0 +1,29 @@
1
+ import type { ModuleSetupContext } from "@comity/composition/setup";
2
+ import type { PaymentProvider } from "../contracts/payment-provider.js";
3
+ import type { PAYMENT_PROVIDER_TOKEN } from "./constants.js";
4
+ /** Hooks exposed by the module */
5
+ export interface PaymentModuleHooks {
6
+ /** Called when the payment module is being configured. */
7
+ "@comity/payment:configuring": PaymentModuleOptions;
8
+ /** Called when the payment module has been initialized. */
9
+ "@comity/payment:initialized": undefined;
10
+ [key: string]: unknown;
11
+ }
12
+ /** Events emitted by the module */
13
+ export type PaymentModuleEvents = {};
14
+ /**
15
+ * Services exposed by the module
16
+ */
17
+ export type PaymentModuleServices = {
18
+ /** Payment provider resolver token */
19
+ [PAYMENT_PROVIDER_TOKEN]: PaymentProvider;
20
+ };
21
+ /**
22
+ * Context provided to the payment module setup function.
23
+ */
24
+ export interface PaymentModuleContext extends ModuleSetupContext<PaymentModuleServices, PaymentModuleEvents, PaymentModuleHooks> {
25
+ }
26
+ export type PaymentModuleOptions = {
27
+ /** The payment provider to use. Required for production. */
28
+ provider?: import("../contracts/payment-provider").PaymentProvider;
29
+ };
package/package.json ADDED
@@ -0,0 +1,101 @@
1
+ {
2
+ "name": "@comity/payment",
3
+ "version": "0.9.0",
4
+ "description": "Payment domain abstractions for Comity.",
5
+ "type": "module",
6
+ "private": false,
7
+ "author": "Filippo Bovo <hello@filippobovo.com>",
8
+ "license": "MIT",
9
+ "comity": {
10
+ "layer": "core"
11
+ },
12
+ "homepage": "https://github.com/comityjs/framework#readme",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/comityjs/framework.git"
16
+ },
17
+ "bugs": {
18
+ "url": "https://github.com/comityjs/framework/issues"
19
+ },
20
+ "engines": {
21
+ "node": ">=24.0.0"
22
+ },
23
+ "keywords": [
24
+ "comity",
25
+ "comityjs",
26
+ "commerce",
27
+ "payment",
28
+ "typescript"
29
+ ],
30
+ "files": [
31
+ "./dist",
32
+ "!./dist/**/*.map"
33
+ ],
34
+ "main": "./dist/cjs/index.js",
35
+ "module": "./dist/esm/index.js",
36
+ "types": "./dist/types/index.d.ts",
37
+ "exports": {
38
+ ".": {
39
+ "import": {
40
+ "types": "./dist/types/index.d.ts",
41
+ "default": "./dist/esm/index.js"
42
+ },
43
+ "require": {
44
+ "types": "./dist/types/index.d.ts",
45
+ "default": "./dist/cjs/index.js"
46
+ }
47
+ },
48
+ "./errors": {
49
+ "import": {
50
+ "types": "./dist/types/errors/index.d.ts",
51
+ "default": "./dist/esm/errors/index.js"
52
+ },
53
+ "require": {
54
+ "types": "./dist/types/errors/index.d.ts",
55
+ "default": "./dist/cjs/errors/index.js"
56
+ }
57
+ },
58
+ "./setup": {
59
+ "import": {
60
+ "types": "./dist/types/setup/index.d.ts",
61
+ "default": "./dist/esm/setup/index.js"
62
+ },
63
+ "require": {
64
+ "types": "./dist/types/setup/index.d.ts",
65
+ "default": "./dist/cjs/setup/index.js"
66
+ }
67
+ },
68
+ "./package.json": "./package.json"
69
+ },
70
+ "typesVersions": {
71
+ "*": {
72
+ "errors": [
73
+ "./dist/types/errors/index.d.ts"
74
+ ],
75
+ "setup": [
76
+ "./dist/types/setup/index.d.ts"
77
+ ]
78
+ }
79
+ },
80
+ "publishConfig": {
81
+ "registry": "https://registry.npmjs.org",
82
+ "access": "public"
83
+ },
84
+ "sideEffects": false,
85
+ "dependencies": {
86
+ "@comity/pricing": "0.9.0",
87
+ "@comity/primitives": "0.9.0",
88
+ "@comity/composition": "0.9.0"
89
+ },
90
+ "devDependencies": {
91
+ "@types/node": "^24.13.4",
92
+ "typescript": "^5.9.3"
93
+ },
94
+ "scripts": {
95
+ "build": "node ../../scripts/build.mjs",
96
+ "dev": "node ../../scripts/build.mjs --watch",
97
+ "test": "vitest run --coverage",
98
+ "type-check": "tsc -p tsconfig.json --noEmit",
99
+ "lint": "eslint --ext .ts src"
100
+ }
101
+ }