ppussh 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 (46) hide show
  1. package/README.md +192 -0
  2. package/dist/accounts/index.d.ts +4 -0
  3. package/dist/accounts/index.d.ts.map +1 -0
  4. package/dist/accounts/index.js +8 -0
  5. package/dist/accounts/index.js.map +1 -0
  6. package/dist/accounts/namespace.d.ts +164 -0
  7. package/dist/accounts/namespace.d.ts.map +1 -0
  8. package/dist/accounts/namespace.js +293 -0
  9. package/dist/accounts/namespace.js.map +1 -0
  10. package/dist/accounts/types.d.ts +81 -0
  11. package/dist/accounts/types.d.ts.map +1 -0
  12. package/dist/accounts/types.js +14 -0
  13. package/dist/accounts/types.js.map +1 -0
  14. package/dist/client.d.ts +67 -0
  15. package/dist/client.d.ts.map +1 -0
  16. package/dist/client.js +81 -0
  17. package/dist/client.js.map +1 -0
  18. package/dist/errors.d.ts +81 -0
  19. package/dist/errors.d.ts.map +1 -0
  20. package/dist/errors.js +94 -0
  21. package/dist/errors.js.map +1 -0
  22. package/dist/http.d.ts +30 -0
  23. package/dist/http.d.ts.map +1 -0
  24. package/dist/http.js +169 -0
  25. package/dist/http.js.map +1 -0
  26. package/dist/index.d.ts +47 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +58 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/payments/index.d.ts +3 -0
  31. package/dist/payments/index.d.ts.map +1 -0
  32. package/dist/payments/index.js +6 -0
  33. package/dist/payments/index.js.map +1 -0
  34. package/dist/payments/namespace.d.ts +146 -0
  35. package/dist/payments/namespace.d.ts.map +1 -0
  36. package/dist/payments/namespace.js +229 -0
  37. package/dist/payments/namespace.js.map +1 -0
  38. package/dist/payments/types.d.ts +98 -0
  39. package/dist/payments/types.d.ts.map +1 -0
  40. package/dist/payments/types.js +10 -0
  41. package/dist/payments/types.js.map +1 -0
  42. package/dist/webhooks.d.ts +56 -0
  43. package/dist/webhooks.d.ts.map +1 -0
  44. package/dist/webhooks.js +67 -0
  45. package/dist/webhooks.js.map +1 -0
  46. package/package.json +54 -0
@@ -0,0 +1,229 @@
1
+ "use strict";
2
+ // ppussh/src/payments/namespace.ts
3
+ /**
4
+ * PaymentsNamespace — customer, subscription, and plan operations.
5
+ *
6
+ * Auth model:
7
+ * - Customer + subscription endpoints (POST /customers, POST /subscriptions, etc.)
8
+ * are unauthenticated at the HTTP level — the Payments service validates the
9
+ * owner_user_id against Accounts internally.
10
+ * - Admin endpoints (listPlans, getMrr) require the payments ADMIN_API_KEY
11
+ * sent as the `X-Admin-Key` header. Set once at PpusshClient construction
12
+ * time via `paymentsAdminKey`.
13
+ *
14
+ * Idempotency:
15
+ * - createSubscription() requires a caller-supplied idempotencyKey.
16
+ * Retry with the *same* key after a 502 — the server guarantees exactly-once creation.
17
+ * - createCustomer() is idempotent on (owner_user_id, workspace_id) — no key needed.
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.PaymentsNamespace = void 0;
21
+ const errors_1 = require("../errors");
22
+ class PaymentsNamespace {
23
+ constructor(transport, options = {}) {
24
+ this._http = transport;
25
+ this._adminKey = options.adminKey ?? null;
26
+ }
27
+ // ── Customers ──────────────────────────────────────────────────────────────
28
+ /**
29
+ * Create (or retrieve) a Payments customer for a PPUSSH user.
30
+ *
31
+ * Idempotent on (owner_user_id, workspace_id) — if a customer already exists
32
+ * for that pair, the existing record is returned without creating a duplicate.
33
+ *
34
+ * @param ownerUserId UUID string of the Accounts user (from TokenResponse.user.id).
35
+ * @param workspaceId UUID string of the workspace, or null for a personal account.
36
+ * @param billingEmail Optional billing email — falls back to the user's account email.
37
+ * @param metadata Arbitrary key/value pairs stored alongside the customer record.
38
+ * @throws PpusshPaymentError code="accounts_user_not_found" if ownerUserId doesn't exist.
39
+ */
40
+ async createCustomer(ownerUserId, options = {}) {
41
+ const body = {
42
+ owner_user_id: ownerUserId,
43
+ workspace_id: options.workspaceId ?? undefined,
44
+ billing_email: options.billingEmail ?? undefined,
45
+ metadata: options.metadata ?? undefined,
46
+ };
47
+ const response = await this._http.request("POST", "/customers", {
48
+ json: body,
49
+ isPayments: true,
50
+ });
51
+ return response.data;
52
+ }
53
+ /**
54
+ * Retrieve a Payments customer by their Payments UUID.
55
+ *
56
+ * @throws PpusshPaymentError code="customer_not_found" on 404.
57
+ */
58
+ async getCustomer(customerId) {
59
+ const response = await this._http.request("GET", `/customers/${customerId}`, {
60
+ isPayments: true,
61
+ });
62
+ return response.data;
63
+ }
64
+ // ── Subscriptions ──────────────────────────────────────────────────────────
65
+ /**
66
+ * Create a subscription for a customer on a billing plan.
67
+ *
68
+ * The idempotencyKey guarantees exactly-once creation. On a provider error (502),
69
+ * retry with the **same key** — this is safe.
70
+ *
71
+ * @param customerId UUID from createCustomer().
72
+ * @param paymentProductId UUID of the PaymentProduct (from the admin console).
73
+ * @param planKey Plan identifier, e.g. "pro" or "enterprise".
74
+ * @param idempotencyKey Unique string per subscription attempt (use UUID v4).
75
+ * @param provider "paddle" | "dodo" | null (uses plan default).
76
+ * @param metadata Arbitrary key/value pairs.
77
+ * @throws PpusshPaymentError Various codes; see error.code for specifics.
78
+ */
79
+ async createSubscription(options) {
80
+ const response = await this._http.request("POST", "/subscriptions", {
81
+ json: {
82
+ customer_id: options.customerId,
83
+ payment_product_id: options.paymentProductId,
84
+ plan_key: options.planKey,
85
+ idempotency_key: options.idempotencyKey,
86
+ ...(options.provider != null && { provider: options.provider }),
87
+ ...(options.metadata != null && { metadata: options.metadata }),
88
+ },
89
+ isPayments: true,
90
+ });
91
+ return response.data;
92
+ }
93
+ /**
94
+ * List subscriptions for a customer, with optional status filter.
95
+ *
96
+ * @param customerId UUID string.
97
+ * @param status Filter by status: "active", "cancelled", "trialing", etc.
98
+ * @param page 1-indexed page number (default 1).
99
+ * @param pageSize Number of results per page, max 100 (default 20).
100
+ */
101
+ async listSubscriptions(customerId, options = {}) {
102
+ const params = {
103
+ customer_id: customerId,
104
+ page: options.page ?? 1,
105
+ page_size: options.pageSize ?? 20,
106
+ };
107
+ if (options.status)
108
+ params["status"] = options.status;
109
+ const response = await this._http.request("GET", "/subscriptions", {
110
+ params,
111
+ isPayments: true,
112
+ });
113
+ return response.data;
114
+ }
115
+ /**
116
+ * Retrieve a single subscription by its Payments UUID.
117
+ *
118
+ * @throws PpusshPaymentError code="subscription_not_found" on 404.
119
+ */
120
+ async getSubscription(subscriptionId) {
121
+ const response = await this._http.request("GET", `/subscriptions/${subscriptionId}`, { isPayments: true });
122
+ return response.data;
123
+ }
124
+ /**
125
+ * Cancel a subscription.
126
+ *
127
+ * Idempotent — cancelling an already-cancelled subscription returns the
128
+ * existing record without error.
129
+ *
130
+ * @param subscriptionId UUID string.
131
+ * @param cancelImmediately If true, cancel at once.
132
+ * If false (default), cancel at end of current billing period.
133
+ */
134
+ async cancelSubscription(subscriptionId, options = {}) {
135
+ const response = await this._http.request("DELETE", `/subscriptions/${subscriptionId}`, {
136
+ json: { cancel_immediately: options.cancelImmediately ?? false },
137
+ isPayments: true,
138
+ });
139
+ return response.data;
140
+ }
141
+ // ── Plans (admin) ──────────────────────────────────────────────────────────
142
+ /**
143
+ * List all billing plans for a Payments product.
144
+ *
145
+ * Requires the `paymentsAdminKey` set on PpusshClient construction.
146
+ * Plans with status "archived" are included — filter client-side if needed.
147
+ *
148
+ * @param paymentProductId UUID string of the PaymentProduct.
149
+ * @throws PpusshPaymentError code="product_not_found" on 404.
150
+ * @throws Error If no paymentsAdminKey was provided at construction.
151
+ */
152
+ async listPlans(paymentProductId) {
153
+ this._requireAdminKey("listPlans");
154
+ const response = await this._http.request("GET", `/admin/products/${paymentProductId}/plans`, {
155
+ headers: { "X-Admin-Key": this._adminKey },
156
+ isPayments: true,
157
+ });
158
+ return response.data;
159
+ }
160
+ /**
161
+ * Look up a Payments product by its Accounts product ID.
162
+ *
163
+ * Returns null if the product has not yet been registered in Payments
164
+ * (HTTP 404 is treated as a non-exceptional "not registered yet" state).
165
+ *
166
+ * @throws Error If no paymentsAdminKey was provided at construction.
167
+ */
168
+ async getProductByAccountsId(accountsProductId) {
169
+ this._requireAdminKey("getProductByAccountsId");
170
+ try {
171
+ const response = await this._http.request("GET", `/admin/products/by-accounts-id/${accountsProductId}`, {
172
+ headers: { "X-Admin-Key": this._adminKey },
173
+ isPayments: true,
174
+ });
175
+ return response.data;
176
+ }
177
+ catch (err) {
178
+ if (err instanceof errors_1.PpusshPaymentError && err.statusCode === 404) {
179
+ return null;
180
+ }
181
+ throw err;
182
+ }
183
+ }
184
+ // ── Analytics (admin) ──────────────────────────────────────────────────────
185
+ /**
186
+ * Fetch Monthly Recurring Revenue breakdown.
187
+ *
188
+ * Requires `paymentsAdminKey`.
189
+ *
190
+ * @param productId Filter to a specific product UUID (optional).
191
+ * @param startDate ISO date string e.g. "2025-01-01" (optional).
192
+ * @param endDate ISO date string e.g. "2025-12-31" (optional).
193
+ */
194
+ async getMrr(options = {}) {
195
+ this._requireAdminKey("getMrr");
196
+ const params = {};
197
+ if (options.productId)
198
+ params["product_id"] = options.productId;
199
+ if (options.startDate)
200
+ params["start_date"] = options.startDate;
201
+ if (options.endDate)
202
+ params["end_date"] = options.endDate;
203
+ const response = await this._http.request("GET", "/admin/analytics/mrr", {
204
+ headers: { "X-Admin-Key": this._adminKey },
205
+ params,
206
+ isPayments: true,
207
+ });
208
+ return response.data;
209
+ }
210
+ // ── Billing portal (stub) ──────────────────────────────────────────────────
211
+ /**
212
+ * Generate a hosted billing portal URL for a customer.
213
+ *
214
+ * @throws Error This feature is not yet implemented in the Payments backend.
215
+ */
216
+ async getBillingPortal(_customerId, _options = {}) {
217
+ throw new Error("getBillingPortal() is not yet available. " +
218
+ "The Payments backend endpoint has not been implemented.");
219
+ }
220
+ // ── Internal helpers ───────────────────────────────────────────────────────
221
+ _requireAdminKey(method) {
222
+ if (!this._adminKey) {
223
+ throw new Error(`payments.${method}() requires a paymentsAdminKey. ` +
224
+ "Pass paymentsAdminKey: '...' to PpusshClient().");
225
+ }
226
+ }
227
+ }
228
+ exports.PaymentsNamespace = PaymentsNamespace;
229
+ //# sourceMappingURL=namespace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"namespace.js","sourceRoot":"","sources":["../../src/payments/namespace.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * TypeScript interfaces for every response shape returned by the Payments service.
3
+ *
4
+ * Mirror of the Python SDK's payments/models.py — kept in sync manually.
5
+ * Monetary amounts are always integer cents — never float.
6
+ */
7
+ /** Response from POST /customers or GET /customers/{id}. */
8
+ export interface CustomerResponse {
9
+ id: string;
10
+ owner_user_id: string;
11
+ workspace_id: string | null;
12
+ provider_customer_ids: Record<string, string>;
13
+ billing_email: string | null;
14
+ created_at: string;
15
+ }
16
+ /** Request body for POST /customers. */
17
+ export interface CustomerCreateRequest {
18
+ owner_user_id: string;
19
+ workspace_id?: string | null;
20
+ billing_email?: string | null;
21
+ metadata?: Record<string, unknown> | null;
22
+ }
23
+ /** Response from GET /admin/products/{id}/plans. */
24
+ export interface PlanResponse {
25
+ id: string;
26
+ product_id: string;
27
+ plan_key: string;
28
+ provider_plan_ids: Record<string, string>;
29
+ amount_cents: number;
30
+ currency: string;
31
+ billing_cycle: "monthly" | "yearly";
32
+ status: "active" | "archived";
33
+ created_at: string;
34
+ }
35
+ export type SubscriptionStatus = "trialing" | "active" | "past_due" | "paused" | "cancelled" | "unpaid";
36
+ /** Response from POST /subscriptions or GET /subscriptions/{id}. */
37
+ export interface SubscriptionResponse {
38
+ id: string;
39
+ customer_id: string;
40
+ plan_id: string;
41
+ provider: string;
42
+ provider_subscription_ids: Record<string, string>;
43
+ status: SubscriptionStatus;
44
+ current_period_start: string | null;
45
+ current_period_end: string | null;
46
+ cancelled_at: string | null;
47
+ trial_ends_at: string | null;
48
+ created_at: string;
49
+ updated_at: string;
50
+ }
51
+ /** Paginated response from GET /subscriptions. */
52
+ export interface SubscriptionListResponse {
53
+ items: SubscriptionResponse[];
54
+ total: number;
55
+ page: number;
56
+ page_size: number;
57
+ }
58
+ /** Request body for POST /subscriptions. */
59
+ export interface SubscriptionCreateRequest {
60
+ customer_id: string;
61
+ payment_product_id: string;
62
+ plan_key: string;
63
+ idempotency_key: string;
64
+ provider?: string | null;
65
+ metadata?: Record<string, unknown> | null;
66
+ }
67
+ /** Request body for DELETE /subscriptions/{id}. */
68
+ export interface SubscriptionCancelRequest {
69
+ cancel_immediately?: boolean;
70
+ }
71
+ /** Response from GET /admin/products/by-accounts-id/{id}. */
72
+ export interface PaymentProductResponse {
73
+ id: string;
74
+ accounts_product_id: string;
75
+ name: string;
76
+ description: string | null;
77
+ created_at: string;
78
+ }
79
+ export interface MRRByProduct {
80
+ product_id: string;
81
+ product_name: string;
82
+ mrr_cents: number;
83
+ currency: string;
84
+ }
85
+ export interface MRRByPlan {
86
+ plan_id: string;
87
+ plan_key: string;
88
+ mrr_cents: number;
89
+ currency: string;
90
+ }
91
+ /** Response from GET /admin/analytics/mrr. */
92
+ export interface MRRResponse {
93
+ total_mrr_cents: number;
94
+ currency: string;
95
+ by_product: MRRByProduct[];
96
+ by_plan: MRRByPlan[];
97
+ }
98
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/payments/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ // ppussh/src/payments/types.ts
3
+ /**
4
+ * TypeScript interfaces for every response shape returned by the Payments service.
5
+ *
6
+ * Mirror of the Python SDK's payments/models.py — kept in sync manually.
7
+ * Monetary amounts are always integer cents — never float.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/payments/types.ts"],"names":[],"mappings":";AAAA,+BAA+B;AAC/B;;;;;GAKG"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Webhook signature verification for PPUSSH Accounts events.
3
+ *
4
+ * The Accounts service dispatches signed HTTP POST requests to a URL you register
5
+ * on your product. Each request carries an `X-Webhook-Signature` header in the
6
+ * format `sha256=<hmac-sha256-hex>`.
7
+ *
8
+ * Usage:
9
+ *
10
+ * import { verifyWebhook, WebhookEvent } from "ppussh";
11
+ *
12
+ * app.post("/webhooks/accounts", express.raw({ type: "*\/*" }), (req, res) => {
13
+ * const sig = req.headers["x-webhook-signature"] as string;
14
+ * if (!verifyWebhook(req.body, sig, process.env.ACCOUNTS_CLIENT_SECRET!)) {
15
+ * return res.status(401).send("Invalid signature");
16
+ * }
17
+ * const event: WebhookEvent = JSON.parse(req.body);
18
+ * // handle event.type ...
19
+ * res.status(200).send("ok");
20
+ * });
21
+ *
22
+ * Algorithm:
23
+ * HMAC-SHA256 over the raw request body, using the product's client_secret as
24
+ * the key. The digest is hex-encoded and prefixed with `sha256=`.
25
+ * Comparison uses crypto.timingSafeEqual to prevent timing attacks.
26
+ */
27
+ export type WebhookEventType = "user.created" | "user.email_verified" | "user.updated" | "user.deleted" | "user.social_linked" | "user.consent_granted" | "session.revoked";
28
+ /**
29
+ * Parsed payload of a PPUSSH Accounts webhook request.
30
+ *
31
+ * Parse after verifying the signature:
32
+ *
33
+ * const event: WebhookEvent = JSON.parse(rawBody);
34
+ */
35
+ export interface WebhookEvent {
36
+ type: WebhookEventType;
37
+ user_id: string;
38
+ email: string;
39
+ product_id: string;
40
+ timestamp: string;
41
+ }
42
+ /**
43
+ * Verify the HMAC-SHA256 signature on an Accounts webhook request.
44
+ *
45
+ * @param rawBody The raw (unparsed) request body as a Buffer or string.
46
+ * Do **not** JSON.parse before passing in.
47
+ * @param signatureHeader The value of the `X-Webhook-Signature` header,
48
+ * e.g. `"sha256=abcdef1234..."`.
49
+ * @param clientSecret Your product's `client_secret` string (from the Accounts
50
+ * admin console). This is the HMAC key.
51
+ * @returns `true` if the signature is valid, `false` otherwise.
52
+ * Returns `false` (not throws) for malformed or missing signatures so
53
+ * callers can safely use the return value as a boolean gate.
54
+ */
55
+ export declare function verifyWebhook(rawBody: Buffer | string, signatureHeader: string, clientSecret: string): boolean;
56
+ //# sourceMappingURL=webhooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAMH,MAAM,MAAM,gBAAgB,GACxB,cAAc,GACd,qBAAqB,GACrB,cAAc,GACd,cAAc,GACd,oBAAoB,GACpB,sBAAsB,GACtB,iBAAiB,CAAC;AAItB;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAID;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,GAAG,MAAM,EACxB,eAAe,EAAE,MAAM,EACvB,YAAY,EAAE,MAAM,GACnB,OAAO,CAuBT"}
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ // ppussh/src/webhooks.ts
3
+ /**
4
+ * Webhook signature verification for PPUSSH Accounts events.
5
+ *
6
+ * The Accounts service dispatches signed HTTP POST requests to a URL you register
7
+ * on your product. Each request carries an `X-Webhook-Signature` header in the
8
+ * format `sha256=<hmac-sha256-hex>`.
9
+ *
10
+ * Usage:
11
+ *
12
+ * import { verifyWebhook, WebhookEvent } from "ppussh";
13
+ *
14
+ * app.post("/webhooks/accounts", express.raw({ type: "*\/*" }), (req, res) => {
15
+ * const sig = req.headers["x-webhook-signature"] as string;
16
+ * if (!verifyWebhook(req.body, sig, process.env.ACCOUNTS_CLIENT_SECRET!)) {
17
+ * return res.status(401).send("Invalid signature");
18
+ * }
19
+ * const event: WebhookEvent = JSON.parse(req.body);
20
+ * // handle event.type ...
21
+ * res.status(200).send("ok");
22
+ * });
23
+ *
24
+ * Algorithm:
25
+ * HMAC-SHA256 over the raw request body, using the product's client_secret as
26
+ * the key. The digest is hex-encoded and prefixed with `sha256=`.
27
+ * Comparison uses crypto.timingSafeEqual to prevent timing attacks.
28
+ */
29
+ Object.defineProperty(exports, "__esModule", { value: true });
30
+ exports.verifyWebhook = verifyWebhook;
31
+ const crypto_1 = require("crypto");
32
+ // ── Signature verification ────────────────────────────────────────────────────
33
+ /**
34
+ * Verify the HMAC-SHA256 signature on an Accounts webhook request.
35
+ *
36
+ * @param rawBody The raw (unparsed) request body as a Buffer or string.
37
+ * Do **not** JSON.parse before passing in.
38
+ * @param signatureHeader The value of the `X-Webhook-Signature` header,
39
+ * e.g. `"sha256=abcdef1234..."`.
40
+ * @param clientSecret Your product's `client_secret` string (from the Accounts
41
+ * admin console). This is the HMAC key.
42
+ * @returns `true` if the signature is valid, `false` otherwise.
43
+ * Returns `false` (not throws) for malformed or missing signatures so
44
+ * callers can safely use the return value as a boolean gate.
45
+ */
46
+ function verifyWebhook(rawBody, signatureHeader, clientSecret) {
47
+ if (!signatureHeader.startsWith("sha256=")) {
48
+ return false;
49
+ }
50
+ try {
51
+ const expectedDigest = (0, crypto_1.createHmac)("sha256", clientSecret)
52
+ .update(rawBody)
53
+ .digest("hex");
54
+ const expected = `sha256=${expectedDigest}`;
55
+ const expectedBuf = Buffer.from(expected);
56
+ const receivedBuf = Buffer.from(signatureHeader);
57
+ // Buffers must be the same length for timingSafeEqual
58
+ if (expectedBuf.length !== receivedBuf.length) {
59
+ return false;
60
+ }
61
+ return (0, crypto_1.timingSafeEqual)(expectedBuf, receivedBuf);
62
+ }
63
+ catch {
64
+ return false;
65
+ }
66
+ }
67
+ //# sourceMappingURL=webhooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":";AAAA,yBAAyB;AACzB;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;;AA+CH,sCA2BC;AAxED,mCAAqD;AA8BrD,iFAAiF;AAEjF;;;;;;;;;;;;GAYG;AACH,SAAgB,aAAa,CAC3B,OAAwB,EACxB,eAAuB,EACvB,YAAoB;IAEpB,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC;QACH,MAAM,cAAc,GAAG,IAAA,mBAAU,EAAC,QAAQ,EAAE,YAAY,CAAC;aACtD,MAAM,CAAC,OAAO,CAAC;aACf,MAAM,CAAC,KAAK,CAAC,CAAC;QACjB,MAAM,QAAQ,GAAG,UAAU,cAAc,EAAE,CAAC;QAE5C,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC1C,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAEjD,sDAAsD;QACtD,IAAI,WAAW,CAAC,MAAM,KAAK,WAAW,CAAC,MAAM,EAAE,CAAC;YAC9C,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO,IAAA,wBAAe,EAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "ppussh",
3
+ "version": "0.1.0",
4
+ "description": "PPUSSH Ecosystem SDK — Accounts (OIDC) + Payments client for TypeScript/JavaScript",
5
+ "license": "MIT",
6
+ "author": "PPUSSH <dev@ppussh.com>",
7
+ "homepage": "https://github.com/ppussh/ppussh-ts#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/ppussh/ppussh-ts.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/ppussh/ppussh-ts/issues"
14
+ },
15
+ "keywords": [
16
+ "ppussh",
17
+ "oidc",
18
+ "oauth2",
19
+ "authentication",
20
+ "payments",
21
+ "subscriptions",
22
+ "sdk"
23
+ ],
24
+ "main": "dist/index.js",
25
+ "types": "dist/index.d.ts",
26
+ "exports": {
27
+ ".": {
28
+ "import": "./dist/index.js",
29
+ "require": "./dist/index.js",
30
+ "types": "./dist/index.d.ts"
31
+ }
32
+ },
33
+ "files": [
34
+ "dist",
35
+ "README.md",
36
+ "LICENSE"
37
+ ],
38
+ "scripts": {
39
+ "build": "tsc",
40
+ "typecheck": "tsc --noEmit",
41
+ "clean": "rm -rf dist",
42
+ "prepublishOnly": "npm run build"
43
+ },
44
+ "dependencies": {
45
+ "axios": "^1.13.6"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^20.19.37",
49
+ "typescript": "^5.9.3"
50
+ },
51
+ "engines": {
52
+ "node": ">=18"
53
+ }
54
+ }