@basaltkit/subscriptions 1.0.0 → 1.0.1

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Machize Contributors
3
+ Copyright (c) 2026 Basalt Contributors
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/dist/index.d.ts CHANGED
@@ -261,6 +261,95 @@ declare class FakeBillingGateway implements BillingGateway {
261
261
  verifyWebhook(rawBody: string, signature: string | undefined): WebhookEvent;
262
262
  }
263
263
 
264
+ /**
265
+ * One-off / reference / mobile-money payments — the model used by Angolan
266
+ * providers (ProxyPay, EMIS/Multicaixa, AppyPay, UNITEL Money), where there's no
267
+ * card-on-file recurring charge or self-service portal. This complements
268
+ * `BillingGateway` (card subscriptions). Recurring billing is modelled by
269
+ * creating one payment per period (invoice → reference → webhook confirms → the
270
+ * period is activated).
271
+ */
272
+ /** A one-off payment request handed to a `PaymentGateway`. */
273
+ interface PaymentRequest {
274
+ /** Who is paying — a tenant/user/customer id you reconcile against. */
275
+ billableId: string;
276
+ /** Amount in the currency's major unit (e.g. 5000 = 5000,00 Kz). */
277
+ amount: number;
278
+ /** ISO 4217. Defaults to the gateway's own (AOA for Angolan gateways). */
279
+ currency?: string;
280
+ /** Your order/invoice id — for idempotency and reconciliation. */
281
+ reference?: string;
282
+ description?: string;
283
+ customer?: {
284
+ name?: string;
285
+ email?: string;
286
+ phone?: string;
287
+ };
288
+ /** When the reference / request stops being payable (epoch ms). */
289
+ expiresAt?: number;
290
+ /** Passed to the gateway and echoed back on the webhook. */
291
+ metadata?: Record<string, string>;
292
+ }
293
+ /** How the customer is told to pay — a reference, a redirect, or a push. */
294
+ interface PaymentInstruction {
295
+ /** The gateway's id for this payment (status checks + reconciliation). */
296
+ id: string;
297
+ status: 'pending' | 'paid' | 'failed';
298
+ /** Reference-based (Multicaixa/EMIS): the entity + reference to pay. */
299
+ reference?: {
300
+ entity: string;
301
+ reference: string;
302
+ amount: number;
303
+ };
304
+ /** Redirect-based (hosted page): send the customer here. */
305
+ url?: string;
306
+ /** Push-based (mobile money): a prompt was sent to this phone. */
307
+ push?: {
308
+ phone: string;
309
+ };
310
+ /** The raw gateway payload, for logging/debugging. */
311
+ raw?: unknown;
312
+ }
313
+ /** A payment webhook, translated to domain terms — gateway payloads never leak. */
314
+ interface PaymentEvent {
315
+ /** Unique id at the gateway — for idempotent processing. */
316
+ id: string;
317
+ type: 'payment.succeeded' | 'payment.failed';
318
+ /** The gateway payment/reference id from `createPayment`. */
319
+ paymentId: string;
320
+ amount: number;
321
+ /** From the request metadata, when the gateway echoes it back. */
322
+ billableId?: string;
323
+ reference?: string;
324
+ raw?: unknown;
325
+ }
326
+ /**
327
+ * Payment gateway driver contract for one-off / reference / mobile-money
328
+ * charges. The app talks to Basalt; only drivers talk to ProxyPay / EMIS / etc.
329
+ * A driver translates raw webhook payloads into `PaymentEvent`.
330
+ */
331
+ interface PaymentGateway {
332
+ readonly name: string;
333
+ /** Create a payment — returns a reference, a redirect URL, or a push. */
334
+ createPayment(request: PaymentRequest): Promise<PaymentInstruction>;
335
+ /**
336
+ * Verifies the signature and translates the payload. Throws
337
+ * `WebhookInvalidError` on a bad signature; returns null for a verified event
338
+ * that isn't a payment (gateways emit event types we don't act on).
339
+ */
340
+ verifyWebhook(rawBody: string, signature: string | undefined): PaymentEvent | null;
341
+ /** Poll a payment's status — a fallback when you can't receive webhooks. */
342
+ getPayment?(id: string): Promise<PaymentInstruction>;
343
+ }
344
+ /** Controllable in-process payment gateway — the test/dev driver. */
345
+ declare class FakePaymentGateway implements PaymentGateway {
346
+ readonly name = "fake";
347
+ readonly payments: PaymentRequest[];
348
+ private counter;
349
+ createPayment(request: PaymentRequest): Promise<PaymentInstruction>;
350
+ verifyWebhook(rawBody: string, signature: string | undefined): PaymentEvent;
351
+ }
352
+
264
353
  declare class StripeRequestError extends BasaltError {
265
354
  readonly httpStatus: number;
266
355
  constructor(httpStatus: number, message: string);
@@ -473,4 +562,4 @@ declare function billingRoutes(options: BillingRoutesOptions): BasaltRoute[];
473
562
  */
474
563
  declare function billingWebhookRoute(gateway: BillingGateway): BasaltRoute;
475
564
 
476
- export { type BillingGateway, type BillingPeriod, type BillingRoutesOptions, type CheckoutInput, type CreateSubscriptionInput, FakeBillingGateway, FeatureUnavailableError, type FeatureValue, GatewayUnsupportedError, MemorySubscriptionStore, MemoryUsageStore, MemoryWebhookStore, type Meter, NotSubscribedError, type PlanDefinition, type Plans, type PortalInput, QuotaExceededError, type RedisLike, RedisUsageStore, type RedisUsageStoreOptions, type RedisWebhookClient, RedisWebhookStore, type RedisWebhookStoreOptions, SUBSCRIPTIONS, StripeBillingGateway, type StripeGatewayOptions, StripeRequestError, type SubscriptionRecord, type SubscriptionStatus, type SubscriptionStore, Subscriptions, type SubscriptionsOptions, type SubscriptionsPluginOptions, type SwapInput, UnknownPlanError, type UsageConsumeResult, type UsageStore, type WebhookEvent, WebhookInvalidError, type WebhookStore, billingRoutes, billingWebhookRoute, definePlans, featureLimit, isMeter, meter, planPrice, subscriptionsPlugin };
565
+ export { type BillingGateway, type BillingPeriod, type BillingRoutesOptions, type CheckoutInput, type CreateSubscriptionInput, FakeBillingGateway, FakePaymentGateway, FeatureUnavailableError, type FeatureValue, GatewayUnsupportedError, MemorySubscriptionStore, MemoryUsageStore, MemoryWebhookStore, type Meter, NotSubscribedError, type PaymentEvent, type PaymentGateway, type PaymentInstruction, type PaymentRequest, type PlanDefinition, type Plans, type PortalInput, QuotaExceededError, type RedisLike, RedisUsageStore, type RedisUsageStoreOptions, type RedisWebhookClient, RedisWebhookStore, type RedisWebhookStoreOptions, SUBSCRIPTIONS, StripeBillingGateway, type StripeGatewayOptions, StripeRequestError, type SubscriptionRecord, type SubscriptionStatus, type SubscriptionStore, Subscriptions, type SubscriptionsOptions, type SubscriptionsPluginOptions, type SwapInput, UnknownPlanError, type UsageConsumeResult, type UsageStore, type WebhookEvent, WebhookInvalidError, type WebhookStore, billingRoutes, billingWebhookRoute, definePlans, featureLimit, isMeter, meter, planPrice, subscriptionsPlugin };
package/dist/index.js CHANGED
@@ -193,6 +193,22 @@ var FakeBillingGateway = class {
193
193
  }
194
194
  };
195
195
 
196
+ // src/payment.ts
197
+ var FakePaymentGateway = class {
198
+ name = "fake";
199
+ payments = [];
200
+ counter = 0;
201
+ async createPayment(request) {
202
+ this.payments.push(request);
203
+ const id = `fake_pay_${++this.counter}`;
204
+ return { id, status: "pending", reference: { entity: "00000", reference: id, amount: request.amount } };
205
+ }
206
+ verifyWebhook(rawBody, signature) {
207
+ if (signature !== "valid") throw new WebhookInvalidError();
208
+ return JSON.parse(rawBody);
209
+ }
210
+ };
211
+
196
212
  // src/drivers/stripe.ts
197
213
  import { createHmac, timingSafeEqual } from "crypto";
198
214
  import { BasaltError as BasaltError3 } from "@basaltkit/core";
@@ -692,6 +708,7 @@ function billingWebhookRoute(gateway) {
692
708
  }
693
709
  export {
694
710
  FakeBillingGateway,
711
+ FakePaymentGateway,
695
712
  FeatureUnavailableError,
696
713
  GatewayUnsupportedError,
697
714
  MemorySubscriptionStore,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@basaltkit/subscriptions",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Billing for Basalt, Cashier/Soulbscription-style: declarative plans, subscriptions with trials, feature flags, usage limits, gateway drivers and idempotent webhooks.",
5
5
  "license": "MIT",
6
6
  "type": "module",