{"_id":"@aboalynx/payment","_rev":"3-fde48ffe78cffe1e1bc25837b606fa26","name":"@aboalynx/payment","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@aboalynx/payment","version":"0.1.0","keywords":["nestjs","payment","payments","payment-gateway","stripe","paypal","checkout","refund","webhooks","typescript"],"author":{"name":"Mohamed Abdelbary"},"license":"MIT","_id":"@aboalynx/payment@0.1.0","maintainers":[{"name":"aboalynx","email":"m.abdelbary.a@gmail.com"}],"homepage":"https://github.com/aboalynx/payment#readme","bugs":{"url":"https://github.com/aboalynx/payment/issues"},"dist":{"shasum":"d30dacc4a5128289157d5b16c8fa9a3da1573ddb","tarball":"https://registry.npmjs.org/@aboalynx/payment/-/payment-0.1.0.tgz","fileCount":39,"integrity":"sha512-V91SKS9mKiXPnxEs0tqiq7WnadyBCxJ4NjnkkVT9P4oj/zexrsMUigmlFg4ZvrguYFI18s3EctoeCZqORbIXZw==","signatures":[{"sig":"MEYCIQDc2E38dI3N88SHLFFcTDa9W3didGkFV+xejEjjqmv8nwIhAMwEj8AsU1WEuovDrSt+2wEvqFglIWl6vISjHd+V+efZ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":75350},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=20"},"gitHead":"a1cff4ba42f24719ae9a217a026fcf695363f76a","scripts":{"lint":"eslint .","test":"jest","build":"tsc -p tsconfig.build.json","format":"prettier --write .","prepack":"npm run build","typecheck":"tsc --noEmit","format:check":"prettier --check ."},"_npmUser":{"name":"aboalynx","email":"m.abdelbary.a@gmail.com"},"repository":{"url":"git+https://github.com/aboalynx/payment.git","type":"git"},"_npmVersion":"11.16.0","description":"Gateway-agnostic payment processing for NestJS. One interface for Stripe and PayPal, designed to be extended.","directories":{},"_nodeVersion":"24.2.0","dependencies":{"stripe":"^22.5.0","reflect-metadata":"^0.2.2","@paypal/paypal-server-sdk":"^2.4.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.4.2","nock":"^14.0.17","rxjs":"^7.8.1","eslint":"^10.8.1","ts-jest":"^29.4.12","prettier":"^3.9.6","typescript":"~5.9.3","@types/jest":"^30.0.0","@types/node":"^22.10.2","@nestjs/core":"^11.1.29","@nestjs/common":"^11.1.29","@nestjs/testing":"^11.1.29","typescript-eslint":"^8.67.0"},"peerDependencies":{"@nestjs/core":"^10.0.0 || ^11.0.0","@nestjs/common":"^10.0.0 || ^11.0.0"},"_npmOperationalInternal":{"tmp":"tmp/payment_0.1.0_1786465146173_0.2519219322630315","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@aboalynx/payment","version":"0.1.1","keywords":["nestjs","payment","payments","payment-gateway","stripe","paypal","checkout","refund","webhooks","typescript"],"author":{"name":"Mohamed Abdelbary"},"license":"MIT","_id":"@aboalynx/payment@0.1.1","maintainers":[{"name":"aboalynx","email":"m.abdelbary.a@gmail.com"}],"homepage":"https://github.com/aboalynx/payment#readme","bugs":{"url":"https://github.com/aboalynx/payment/issues"},"dist":{"shasum":"eeea6b498efd27f5c5daddf6b772e2244e098ddc","tarball":"https://registry.npmjs.org/@aboalynx/payment/-/payment-0.1.1.tgz","fileCount":39,"integrity":"sha512-IVk1fWNm0WhMRXRsXEvkwxI6fhdtzMkqTVDEQZOc1P4AbvYj4/uA3FS3xZXjsx+kvGCXIaNs9QSbr2ZiAienEg==","signatures":[{"sig":"MEUCIQC5iiC6+n7KIUZnYypuPznlHcXXOnrqfChmE9P2IziVWgIgBg/5jJPrO7rc3+2cM8lQixB752/3+FdGa5mU4jczZ00=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aboalynx%2fpayment@0.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":75543},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"b2ec658de88c09245e02479d5c0bb88240d9da2a","scripts":{"lint":"eslint .","test":"jest","build":"tsc -p tsconfig.build.json","clean":"rm -rf dist","format":"prettier --write .","prepack":"npm run clean && npm run build","typecheck":"tsc --noEmit","format:check":"prettier --check ."},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:9a45367a-01d6-4d1d-892c-00131d12226f"}},"repository":{"url":"git+https://github.com/aboalynx/payment.git","type":"git"},"_npmVersion":"11.19.0","description":"Gateway-agnostic payment processing for NestJS. One interface for Stripe and PayPal, designed to be extended.","directories":{},"_nodeVersion":"20.20.2","dependencies":{"stripe":"^22.5.0","reflect-metadata":"^0.2.2","@paypal/paypal-server-sdk":"^2.4.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.4.2","nock":"^14.0.17","rxjs":"^7.8.1","eslint":"^10.8.1","ts-jest":"^29.4.12","prettier":"^3.9.6","typescript":"~5.9.3","@types/jest":"^30.0.0","@types/node":"^22.10.2","@nestjs/core":"^11.1.29","@nestjs/common":"^11.1.29","@nestjs/testing":"^11.1.29","typescript-eslint":"^8.67.0"},"peerDependencies":{"@nestjs/core":"^10.0.0 || ^11.0.0","@nestjs/common":"^10.0.0 || ^11.0.0"},"_npmOperationalInternal":{"tmp":"tmp/payment_0.1.1_1786470483710_0.7027831407703553","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@aboalynx/payment","version":"0.2.0","description":"Gateway-agnostic payment processing for NestJS. One interface for Stripe and PayPal, designed to be extended.","license":"MIT","author":{"name":"Mohamed Abdelbary"},"homepage":"https://github.com/aboalynx/payment#readme","repository":{"type":"git","url":"git+https://github.com/aboalynx/payment.git"},"bugs":{"url":"https://github.com/aboalynx/payment/issues"},"keywords":["nestjs","payment","payments","payment-gateway","stripe","paypal","checkout","refund","webhooks","typescript"],"main":"dist/index.js","types":"dist/index.d.ts","publishConfig":{"access":"public"},"engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"scripts":{"clean":"rm -rf dist","build":"tsc -p tsconfig.build.json","prepack":"npm run clean && npm run build","typecheck":"tsc --noEmit","lint":"eslint .","format":"prettier --write .","format:check":"prettier --check .","test":"jest"},"peerDependencies":{"@nestjs/common":"^10.0.0 || ^11.0.0","@nestjs/core":"^10.0.0 || ^11.0.0"},"dependencies":{"@paypal/paypal-server-sdk":"^2.4.0","reflect-metadata":"^0.2.2","stripe":"^22.5.0"},"devDependencies":{"@nestjs/common":"^11.1.29","@nestjs/core":"^11.1.29","@nestjs/testing":"^11.1.29","@types/jest":"^30.0.0","@types/node":"^22.10.2","eslint":"^10.8.1","jest":"^30.4.2","nock":"^14.0.17","prettier":"^3.9.6","rxjs":"^7.8.1","ts-jest":"^29.4.12","typescript":"~5.9.3","typescript-eslint":"^8.67.0"},"gitHead":"848048226acd8d31423373b6a49fdc57f670ac2c","_id":"@aboalynx/payment@0.2.0","_nodeVersion":"20.20.2","_npmVersion":"11.19.1","dist":{"integrity":"sha512-fYh+vfgrYIoqHcZIz1SiL/DSm87t1m+TiQBIKuqCbbfKq1uJl6USggb3it1mUrs6pcSEjVq9ndHf+HtFHbcrhQ==","shasum":"8a93c1d8f8dea02f77db466bf2683c131de9c418","tarball":"https://registry.npmjs.org/@aboalynx/payment/-/payment-0.2.0.tgz","fileCount":39,"unpackedSize":92601,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aboalynx%2fpayment@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIA2AblHnF9H+AsEU5XwbhEX/DkK5BBeuwSe8maRBxU+YAiEAp3TmMH/Ilcpo8c5ptBWA+0U4+9NTm3Cy4rvLVWDZqRg="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:9a45367a-01d6-4d1d-892c-00131d12226f"}},"directories":{},"maintainers":[{"name":"aboalynx","email":"m.abdelbary.a@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/payment_0.2.0_1788813219606_0.9853672097243789"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-11T16:19:06.032Z","modified":"2026-09-07T20:33:40.038Z","0.1.0":"2026-08-11T16:19:06.415Z","0.1.1":"2026-08-11T17:48:03.846Z","0.2.0":"2026-09-07T20:33:39.759Z"},"bugs":{"url":"https://github.com/aboalynx/payment/issues"},"author":{"name":"Mohamed Abdelbary"},"license":"MIT","homepage":"https://github.com/aboalynx/payment#readme","keywords":["nestjs","payment","payments","payment-gateway","stripe","paypal","checkout","refund","webhooks","typescript"],"repository":{"type":"git","url":"git+https://github.com/aboalynx/payment.git"},"description":"Gateway-agnostic payment processing for NestJS. One interface for Stripe and PayPal, designed to be extended.","maintainers":[{"name":"aboalynx","email":"m.abdelbary.a@gmail.com"}],"readme":"# @aboalynx/payment\n\nGateway-agnostic payment processing for NestJS. One interface for Stripe and PayPal, designed to be extended.\n\n[![npm](https://img.shields.io/npm/v/@aboalynx/payment)](https://www.npmjs.com/package/@aboalynx/payment)\n[![CI](https://github.com/aboalynx/payment/actions/workflows/ci.yml/badge.svg)](https://github.com/aboalynx/payment/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n[![Node](https://img.shields.io/badge/node-%3E%3D20-339933.svg)](package.json)\n[![NestJS](https://img.shields.io/badge/nestjs-10%20%7C%2011-e0234e.svg)](package.json)\n\n---\n\n## Why\n\nPayment integrations tend to grow one service class per provider, each shaped by that\nprovider's API. This package puts one vocabulary in front of them:\n\n```ts\nawait payments.checkout('stripe', request);\nawait payments.checkout('paypal', request); // same request, same result shape\nawait payments.checkout(tenant.gateway, request); // provider is now configuration\n```\n\nAdding a provider means implementing the capability interfaces it supports and passing a\nshared contract suite. Calling code does not change.\n\n## Install\n\nPublished from CI with [build provenance](https://www.npmjs.com/package/@aboalynx/payment) —\nthe tarball is cryptographically tied to the commit and workflow that built it.\n\n```bash\nnpm install @aboalynx/payment\n```\n\n`@nestjs/common` and `@nestjs/core` are peer dependencies.\n\n## Quick start\n\n```ts\nimport { Module } from '@nestjs/common';\nimport { PaymentModule, createStripeGateway, createPaypalGateway } from '@aboalynx/payment';\n\n@Module({\n  imports: [\n    PaymentModule.forRoot({\n      gateways: [\n        createStripeGateway({\n          apiKey: process.env.STRIPE_SECRET_KEY!,\n          webhookSecret: process.env.STRIPE_WEBHOOK_SECRET,\n        }),\n        createPaypalGateway({\n          clientId: process.env.PAYPAL_CLIENT_ID!,\n          clientSecret: process.env.PAYPAL_CLIENT_SECRET!,\n          environment: 'sandbox',\n          webhookId: process.env.PAYPAL_WEBHOOK_ID,\n        }),\n      ],\n    }),\n  ],\n})\nexport class AppModule {}\n```\n\nWith credentials from `ConfigService`:\n\n```ts\nPaymentModule.forRootAsync({\n  inject: [ConfigService],\n  useFactory: (config: ConfigService) => ({\n    gateways: [createStripeGateway({ apiKey: config.getOrThrow('STRIPE_SECRET_KEY') })],\n  }),\n});\n```\n\n## Taking a payment\n\n```ts\n@Injectable()\nexport class CheckoutService {\n  constructor(private readonly payments: PaymentService) {}\n\n  async start(order: Order): Promise<string> {\n    const result = await this.payments.checkout('stripe', {\n      reference: order.id,\n      amount: 49.99, // major units — conversion happens inside the gateway\n      currency: 'USD',\n      description: 'Pro plan',\n      successUrl: 'https://example.com/success',\n      cancelUrl: 'https://example.com/cancel',\n    });\n\n    // Persist result.sessionId — you need it to capture.\n    await this.orders.recordSession(order.id, result.sessionId);\n\n    return result.status === 'redirect' ? result.url : result.code;\n  }\n\n  async finish(sessionId: string) {\n    const result = await this.payments.capture('stripe', { sessionId });\n\n    if (result.status === 'paid') {\n      // result.transactionId is what you refund later.\n      await this.orders.markPaid(result.reference!, result.transactionId);\n    }\n  }\n}\n```\n\n`CheckoutResult` and `CaptureResult` are discriminated unions, so the compiler makes you\nhandle every outcome.\n\n## Refunds\n\n```ts\nawait payments.refund('stripe', { transactionId: 'pi_123', currency: 'USD' }); // full\nawait payments.refund('stripe', { transactionId: 'pi_123', currency: 'USD', amount: 5 }); // partial\n```\n\n## Webhooks\n\nThe package verifies signatures; your application owns the route. See\n[docs/webhooks.md](docs/webhooks.md) for a controller you can copy, the raw-body\nrequirement, and the idempotency your application is responsible for.\n\n```ts\nconst event = await payments.verifyWebhook('stripe', {\n  rawBody: request.rawBody, // the unparsed bytes — see docs/webhooks.md\n  headers: request.headers,\n});\n```\n\n## Capabilities\n\nA gateway declares what it supports; asking for anything else throws\n`UnsupportedOperationError` before any network call.\n\n```ts\nif (payments.supports('paypal', 'refund')) {\n  await payments.refund('paypal', { transactionId, currency });\n}\n```\n\n| Capability       | Operations                        | Stripe  |         PayPal          |\n| ---------------- | --------------------------------- | :-----: | :---------------------: |\n| `checkout`       | `checkout`, `capture`             |   ✅    |           ✅            |\n| `refund`         | `refund`                          |   ✅    |           ✅            |\n| `webhooks`       | `verifyWebhook`                   |   ✅    |           ✅            |\n| `paymentMethods` | setup, retrieve, delete           | planned |         planned         |\n| `savedCharge`    | charge a stored method            | planned |         planned         |\n| `platform`       | onboarding, OAuth, account status | planned |         planned         |\n| `tax`            | tax calculation                   | planned | not supported by PayPal |\n\n### capture() is idempotent on both gateways\n\nCalling `capture()` twice for the same session is safe. That takes work to be true:\nStripe's equivalent is a status read that can be repeated freely, while PayPal's\n`captureOrder` moves money and rejects a second call with `ORDER_ALREADY_CAPTURED`. The\nPayPal gateway catches exactly that and reads the existing capture back, so the same call\nmeans the same thing regardless of provider.\n\nThis matters for webhook handlers, which providers retry. A duplicate delivery that\nreaches `capture()` will not double-charge or throw.\n\n## Money\n\nAmounts cross the public API in **major units** — `49.99`, not `4999`. Each gateway\nconverts using the ISO-4217 exponent for the currency: 2 decimals for USD, 3 for KWD,\n0 for JPY. Stripe receives minor units; PayPal receives a decimal string. Neither is\nyour problem.\n\n```ts\nimport { toMinorUnit } from '@aboalynx/payment';\n\ntoMinorUnit(49.99, 'USD'); // 4999\ntoMinorUnit(49.99, 'KWD'); // 49990\ntoMinorUnit(4999, 'JPY'); // 4999\n```\n\n## Events\n\nEvery operation emits a typed event through a publisher you supply. The package depends\non no message broker.\n\n```ts\nPaymentModule.forRoot({\n  gateways: [...],\n  publisher: {\n    publish: async (event) => rabbit.publish('payments', event.name, event),\n  },\n});\n```\n\nPublishing never fails a payment — if the broker is down after a charge succeeded, the\ncharge still succeeded. See [docs/events.md](docs/events.md) for a RabbitMQ example and\nthe transactional outbox pattern you should use in production.\n\n## What this package does not do\n\n- **No HTTP routes.** Routing is your application's. The package never registers a\n  controller.\n- **No persistence.** It takes a request, calls a provider, returns a typed result. What\n  you store is your decision.\n- **No message broker.** Events go through a port you implement.\n\nAll three are deliberate: a library that ships them forces its own routing, storage and\ninfrastructure opinions onto every consumer.\n\n## Extending\n\nAdding a gateway means implementing `PaymentGateway` plus the capability interfaces the\nprovider supports, then passing the shared contract suite. See\n[docs/adding-a-gateway.md](docs/adding-a-gateway.md).\n\n## Documentation\n\n- [Architecture](docs/architecture.md) — the three layers and why the boundaries sit where they do\n- [Adding a gateway](docs/adding-a-gateway.md) — the extension guide\n- [Webhooks](docs/webhooks.md) — raw bodies, verification, idempotency\n- [Events](docs/events.md) — the publisher port, RabbitMQ, the outbox pattern\n\n## Development\n\n```bash\nnpm install\nnpm run typecheck\nnpm run lint\nnpm run format:check\nnpm test\nnpm run build\n```\n\n## License\n\nMIT. See [LICENSE](LICENSE).\n","readmeFilename":"README.md"}