@surgent-dev/surpay-convex 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.
package/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # @surgent-dev/surpay-convex
2
+
3
+ Convex integration for Surpay SDK.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @surgent-dev/surpay-convex
9
+ ```
10
+
11
+ ## Setup
12
+
13
+ Step 1: Create `convex/surpay.ts`
14
+
15
+ ```typescript
16
+ import { Surpay } from "@surgent-dev/surpay-convex";
17
+
18
+ const surpay = new Surpay({
19
+ apiKey: process.env.SURPAY_API_KEY!,
20
+ });
21
+
22
+ export const {
23
+ createCheckout,
24
+ getCustomer,
25
+ listCustomers,
26
+ listSubscriptions,
27
+ } = surpay.api();
28
+ ```
29
+
30
+ Step 2: Set environment variable
31
+
32
+ Add `SURPAY_API_KEY` to your Convex dashboard environment variables.
33
+
34
+ ## Usage
35
+
36
+ ### Create Checkout
37
+
38
+ ```typescript
39
+ import { api } from "./_generated/api";
40
+
41
+ // Inside a Convex action or from client
42
+ const { data, error } = await ctx.runAction(api.surpay.createCheckout, {
43
+ product_id: "prod_123",
44
+ price_id: "price_123",
45
+ success_url: "https://example.com/success",
46
+ cancel_url: "https://example.com/cancel",
47
+ });
48
+
49
+ if (error) {
50
+ console.error(error.message);
51
+ } else {
52
+ console.log(data.checkout_url);
53
+ }
54
+ ```
55
+
56
+ ### Get Customer
57
+
58
+ ```typescript
59
+ import { api } from "./_generated/api";
60
+
61
+ const { data, error } = await ctx.runAction(api.surpay.getCustomer, {
62
+ project_id: "proj_123",
63
+ customer_id: "cust_123",
64
+ });
65
+ ```
66
+
67
+ ### List Subscriptions
68
+
69
+ ```typescript
70
+ import { api } from "./_generated/api";
71
+
72
+ const { data, error } = await ctx.runAction(api.surpay.listSubscriptions, {
73
+ project_id: "proj_123",
74
+ });
75
+ ```
76
+
77
+ ## API Reference
78
+
79
+ | Action | Args | Returns |
80
+ |--------|------|---------|
81
+ | `createCheckout` | `product_id`, `price_id`, `success_url`, `cancel_url` | `{ checkout_url: string, session_id: string }` |
82
+ | `getCustomer` | `project_id`, `customer_id` | `CustomerWithDetails` |
83
+ | `listCustomers` | `project_id` | `Customer[]` |
84
+ | `listSubscriptions` | `project_id` | `Subscription[]` |
@@ -0,0 +1,74 @@
1
+ import { Surpay as SurpayClient } from "@surgent-dev/surpay";
2
+ export type IdentifierOpts = {
3
+ customerId: string;
4
+ customerData?: {
5
+ name?: string;
6
+ email?: string;
7
+ };
8
+ };
9
+ export type SurpayConfig = {
10
+ apiKey: string;
11
+ baseUrl?: string;
12
+ identify: (ctx: any) => Promise<IdentifierOpts | null>;
13
+ };
14
+ type PlainError = {
15
+ message: string;
16
+ code?: string;
17
+ };
18
+ export declare class Surpay {
19
+ private client;
20
+ private options;
21
+ constructor(config: SurpayConfig);
22
+ getIdentifierOpts(ctx: any): Promise<IdentifierOpts | null>;
23
+ getAuthParams({ ctx, requireAuth, }: {
24
+ ctx: any;
25
+ requireAuth?: boolean;
26
+ }): Promise<{
27
+ client: SurpayClient;
28
+ identifierOpts: IdentifierOpts | null;
29
+ }>;
30
+ api(): {
31
+ createCheckout: import("convex/server").RegisteredAction<"public", {
32
+ product_id: string;
33
+ price_id: string;
34
+ success_url: string;
35
+ cancel_url: string;
36
+ }, Promise<{
37
+ data: null;
38
+ error: PlainError;
39
+ } | {
40
+ data: import("@surgent-dev/surpay").CreateCheckoutResponse;
41
+ error: null;
42
+ }>>;
43
+ getCustomer: import("convex/server").RegisteredAction<"public", {
44
+ project_id: string;
45
+ customer_id: string;
46
+ }, Promise<{
47
+ data: null;
48
+ error: PlainError;
49
+ } | {
50
+ data: import("@surgent-dev/surpay").CustomerWithDetails;
51
+ error: null;
52
+ }>>;
53
+ listCustomers: import("convex/server").RegisteredAction<"public", {
54
+ project_id: string;
55
+ }, Promise<{
56
+ data: null;
57
+ error: PlainError;
58
+ } | {
59
+ data: import("@surgent-dev/surpay").Customer[];
60
+ error: null;
61
+ }>>;
62
+ listSubscriptions: import("convex/server").RegisteredAction<"public", {
63
+ project_id: string;
64
+ }, Promise<{
65
+ data: null;
66
+ error: PlainError;
67
+ } | {
68
+ data: import("@surgent-dev/surpay").Subscription[];
69
+ error: null;
70
+ }>>;
71
+ };
72
+ }
73
+ export * from "./types.js";
74
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAE,MAAM,IAAI,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAQ7D,MAAM,MAAM,cAAc,GAAG;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;CACxD,CAAC;AAGF,KAAK,UAAU,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAwBrD,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,OAAO,CAAe;gBAElB,MAAM,EAAE,YAAY;IAQ1B,iBAAiB,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAI3D,aAAa,CAAC,EAClB,GAAG,EACH,WAAkB,GACnB,EAAE;QACD,GAAG,EAAE,GAAG,CAAC;QACT,WAAW,CAAC,EAAE,OAAO,CAAC;KACvB,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,YAAY,CAAC;QAAC,cAAc,EAAE,cAAc,GAAG,IAAI,CAAA;KAAE,CAAC;IAQ5E,GAAG;;;;;;;kBA1CyC,IAAI;mBAAS,UAAU;;;mBAAxC,IAAI;;;;;;kBAAa,IAAI;mBAAS,UAAU;;;mBAAxC,IAAI;;;;;kBAAa,IAAI;mBAAS,UAAU;;;mBAAxC,IAAI;;;;;kBAAa,IAAI;mBAAS,UAAU;;;mBAAxC,IAAI;;;CAyFhC;AAED,cAAc,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Surpay Convex Integration
3
+ *
4
+ * Provides Convex actions that wrap the Surpay SDK with auth context support.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * // convex/surpay.ts
9
+ * import { Surpay } from "@surgent-dev/surpay-convex";
10
+ *
11
+ * const surpay = new Surpay({
12
+ * apiKey: process.env.SURPAY_API_KEY!,
13
+ * identify: async (ctx) => {
14
+ * const identity = await ctx.auth.getUserIdentity();
15
+ * if (!identity) return null;
16
+ * return {
17
+ * customerId: identity.subject,
18
+ * customerData: { name: identity.name, email: identity.email },
19
+ * };
20
+ * },
21
+ * });
22
+ * export const { createCheckout, getCustomer, listCustomers, listSubscriptions } = surpay.api();
23
+ * ```
24
+ */
25
+ import { actionGeneric } from "convex/server";
26
+ import { Surpay as SurpayClient } from "@surgent-dev/surpay";
27
+ import { CreateCheckoutArgs, GetCustomerArgs, ListCustomersArgs, ListSubscriptionsArgs, } from "./types.js";
28
+ function toPlainError(error) {
29
+ if (error instanceof Error) {
30
+ return { message: error.message, code: error.code };
31
+ }
32
+ return { message: String(error) };
33
+ }
34
+ // Wrap SDK calls to ensure errors are plain objects
35
+ async function wrapSdkCall(fn) {
36
+ try {
37
+ const result = await fn();
38
+ if (result.error) {
39
+ return { data: null, error: toPlainError(result.error) };
40
+ }
41
+ return result;
42
+ }
43
+ catch (e) {
44
+ return { data: null, error: toPlainError(e) };
45
+ }
46
+ }
47
+ export class Surpay {
48
+ client;
49
+ options;
50
+ constructor(config) {
51
+ this.options = config;
52
+ this.client = new SurpayClient({
53
+ apiKey: config.apiKey,
54
+ baseUrl: config.baseUrl,
55
+ });
56
+ }
57
+ async getIdentifierOpts(ctx) {
58
+ return await this.options.identify(ctx);
59
+ }
60
+ async getAuthParams({ ctx, requireAuth = true, }) {
61
+ const identifierOpts = await this.getIdentifierOpts(ctx);
62
+ if (requireAuth && !identifierOpts) {
63
+ throw new Error("No customer identifier found");
64
+ }
65
+ return { client: this.client, identifierOpts };
66
+ }
67
+ api() {
68
+ return {
69
+ createCheckout: actionGeneric({
70
+ args: CreateCheckoutArgs,
71
+ handler: async (ctx, args) => {
72
+ const { client, identifierOpts } = await this.getAuthParams({ ctx });
73
+ // Merge customer info from auth context with checkout args
74
+ const checkoutParams = {
75
+ ...args,
76
+ customer_id: identifierOpts.customerId,
77
+ customer_name: identifierOpts.customerData?.name,
78
+ customer_email: identifierOpts.customerData?.email,
79
+ };
80
+ return wrapSdkCall(() => client.checkout.create(checkoutParams));
81
+ },
82
+ }),
83
+ getCustomer: actionGeneric({
84
+ args: GetCustomerArgs,
85
+ handler: async (ctx, args) => {
86
+ const { client, identifierOpts } = await this.getAuthParams({ ctx });
87
+ const customerId = args.customer_id || identifierOpts.customerId;
88
+ return wrapSdkCall(() => client.customers.get(args.project_id, customerId));
89
+ },
90
+ }),
91
+ listCustomers: actionGeneric({
92
+ args: ListCustomersArgs,
93
+ handler: async (ctx, args) => {
94
+ const { client } = await this.getAuthParams({ ctx });
95
+ return wrapSdkCall(() => client.customers.list(args.project_id));
96
+ },
97
+ }),
98
+ listSubscriptions: actionGeneric({
99
+ args: ListSubscriptionsArgs,
100
+ handler: async (ctx, args) => {
101
+ const { client } = await this.getAuthParams({ ctx });
102
+ return wrapSdkCall(() => client.subscriptions.list(args.project_id));
103
+ },
104
+ }),
105
+ };
106
+ }
107
+ }
108
+ export * from "./types.js";
109
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Convex validators for Surpay action arguments
3
+ */
4
+ import { Infer } from "convex/values";
5
+ export declare const CreateCheckoutArgs: import("convex/values").VObject<{
6
+ product_id: string;
7
+ price_id: string;
8
+ success_url: string;
9
+ cancel_url: string;
10
+ }, {
11
+ product_id: import("convex/values").VString<string, "required">;
12
+ price_id: import("convex/values").VString<string, "required">;
13
+ success_url: import("convex/values").VString<string, "required">;
14
+ cancel_url: import("convex/values").VString<string, "required">;
15
+ }, "required", "product_id" | "price_id" | "success_url" | "cancel_url">;
16
+ export type CreateCheckoutArgs = Infer<typeof CreateCheckoutArgs>;
17
+ export declare const ListSubscriptionsArgs: import("convex/values").VObject<{
18
+ project_id: string;
19
+ }, {
20
+ project_id: import("convex/values").VString<string, "required">;
21
+ }, "required", "project_id">;
22
+ export type ListSubscriptionsArgs = Infer<typeof ListSubscriptionsArgs>;
23
+ export declare const GetCustomerArgs: import("convex/values").VObject<{
24
+ project_id: string;
25
+ customer_id: string;
26
+ }, {
27
+ project_id: import("convex/values").VString<string, "required">;
28
+ customer_id: import("convex/values").VString<string, "required">;
29
+ }, "required", "project_id" | "customer_id">;
30
+ export type GetCustomerArgs = Infer<typeof GetCustomerArgs>;
31
+ export declare const ListCustomersArgs: import("convex/values").VObject<{
32
+ project_id: string;
33
+ }, {
34
+ project_id: import("convex/values").VString<string, "required">;
35
+ }, "required", "project_id">;
36
+ export type ListCustomersArgs = Infer<typeof ListCustomersArgs>;
37
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAK,KAAK,EAAE,MAAM,eAAe,CAAC;AAGzC,eAAO,MAAM,kBAAkB;;;;;;;;;;wEAK7B,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAGlE,eAAO,MAAM,qBAAqB;;;;4BAEhC,CAAC;AACH,MAAM,MAAM,qBAAqB,GAAG,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAGxE,eAAO,MAAM,eAAe;;;;;;4CAG1B,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAG5D,eAAO,MAAM,iBAAiB;;;;4BAE5B,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Convex validators for Surpay action arguments
3
+ */
4
+ import { v } from "convex/values";
5
+ // CreateCheckoutRequest: product_id, price_id, success_url, cancel_url (all required)
6
+ export const CreateCheckoutArgs = v.object({
7
+ product_id: v.string(),
8
+ price_id: v.string(),
9
+ success_url: v.string(),
10
+ cancel_url: v.string(),
11
+ });
12
+ // ListSubscriptions: requires project_id
13
+ export const ListSubscriptionsArgs = v.object({
14
+ project_id: v.string(),
15
+ });
16
+ // GetCustomer: requires project_id and customer_id
17
+ export const GetCustomerArgs = v.object({
18
+ project_id: v.string(),
19
+ customer_id: v.string(),
20
+ });
21
+ // ListCustomers: requires project_id
22
+ export const ListCustomersArgs = v.object({
23
+ project_id: v.string(),
24
+ });
25
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,CAAC,EAAS,MAAM,eAAe,CAAC;AAEzC,sFAAsF;AACtF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;CACvB,CAAC,CAAC;AAGH,yCAAyC;AACzC,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;CACvB,CAAC,CAAC;AAGH,mDAAmD;AACnD,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;CACxB,CAAC,CAAC;AAGH,qCAAqC;AACrC,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;CACvB,CAAC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@surgent-dev/surpay-convex",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ }
12
+ },
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "clean": "rm -rf dist"
16
+ },
17
+ "dependencies": {
18
+ "@surgent-dev/surpay": "workspace:*"
19
+ },
20
+ "peerDependencies": {
21
+ "convex": "^1.25.0"
22
+ },
23
+ "devDependencies": {
24
+ "typescript": "^5.9.3",
25
+ "convex": "^1.25.0"
26
+ },
27
+ "publishConfig": {
28
+ "access": "public"
29
+ }
30
+ }
package/src/index.ts ADDED
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Surpay Convex Integration
3
+ *
4
+ * Provides Convex actions that wrap the Surpay SDK with auth context support.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * // convex/surpay.ts
9
+ * import { Surpay } from "@surgent-dev/surpay-convex";
10
+ *
11
+ * const surpay = new Surpay({
12
+ * apiKey: process.env.SURPAY_API_KEY!,
13
+ * identify: async (ctx) => {
14
+ * const identity = await ctx.auth.getUserIdentity();
15
+ * if (!identity) return null;
16
+ * return {
17
+ * customerId: identity.subject,
18
+ * customerData: { name: identity.name, email: identity.email },
19
+ * };
20
+ * },
21
+ * });
22
+ * export const { createCheckout, getCustomer, listCustomers, listSubscriptions } = surpay.api();
23
+ * ```
24
+ */
25
+ import { actionGeneric } from "convex/server";
26
+ import { Surpay as SurpayClient } from "@surgent-dev/surpay";
27
+ import {
28
+ CreateCheckoutArgs,
29
+ GetCustomerArgs,
30
+ ListCustomersArgs,
31
+ ListSubscriptionsArgs,
32
+ } from "./types.js";
33
+
34
+ export type IdentifierOpts = {
35
+ customerId: string;
36
+ customerData?: { name?: string; email?: string };
37
+ };
38
+
39
+ export type SurpayConfig = {
40
+ apiKey: string;
41
+ baseUrl?: string;
42
+ identify: (ctx: any) => Promise<IdentifierOpts | null>;
43
+ };
44
+
45
+ // Convex can't serialize class instances - convert errors to plain objects
46
+ type PlainError = { message: string; code?: string };
47
+
48
+ function toPlainError(error: unknown): PlainError {
49
+ if (error instanceof Error) {
50
+ return { message: error.message, code: (error as { code?: string }).code };
51
+ }
52
+ return { message: String(error) };
53
+ }
54
+
55
+ // Wrap SDK calls to ensure errors are plain objects
56
+ async function wrapSdkCall<T>(
57
+ fn: () => Promise<{ data: T; error: null } | { data: null; error: unknown }>
58
+ ): Promise<{ data: T; error: null } | { data: null; error: PlainError }> {
59
+ try {
60
+ const result = await fn();
61
+ if (result.error) {
62
+ return { data: null, error: toPlainError(result.error) };
63
+ }
64
+ return result as { data: T; error: null };
65
+ } catch (e) {
66
+ return { data: null, error: toPlainError(e) };
67
+ }
68
+ }
69
+
70
+ export class Surpay {
71
+ private client: SurpayClient;
72
+ private options: SurpayConfig;
73
+
74
+ constructor(config: SurpayConfig) {
75
+ this.options = config;
76
+ this.client = new SurpayClient({
77
+ apiKey: config.apiKey,
78
+ baseUrl: config.baseUrl,
79
+ });
80
+ }
81
+
82
+ async getIdentifierOpts(ctx: any): Promise<IdentifierOpts | null> {
83
+ return await this.options.identify(ctx);
84
+ }
85
+
86
+ async getAuthParams({
87
+ ctx,
88
+ requireAuth = true,
89
+ }: {
90
+ ctx: any;
91
+ requireAuth?: boolean;
92
+ }): Promise<{ client: SurpayClient; identifierOpts: IdentifierOpts | null }> {
93
+ const identifierOpts = await this.getIdentifierOpts(ctx);
94
+ if (requireAuth && !identifierOpts) {
95
+ throw new Error("No customer identifier found");
96
+ }
97
+ return { client: this.client, identifierOpts };
98
+ }
99
+
100
+ api() {
101
+ return {
102
+ createCheckout: actionGeneric({
103
+ args: CreateCheckoutArgs,
104
+ handler: async (ctx, args) => {
105
+ const { client, identifierOpts } = await this.getAuthParams({ ctx });
106
+ // Merge customer info from auth context with checkout args
107
+ const checkoutParams = {
108
+ ...args,
109
+ customer_id: identifierOpts!.customerId,
110
+ customer_name: identifierOpts!.customerData?.name,
111
+ customer_email: identifierOpts!.customerData?.email,
112
+ };
113
+ return wrapSdkCall(() =>
114
+ client.checkout.create(checkoutParams as Parameters<typeof client.checkout.create>[0])
115
+ );
116
+ },
117
+ }),
118
+
119
+ getCustomer: actionGeneric({
120
+ args: GetCustomerArgs,
121
+ handler: async (ctx, args) => {
122
+ const { client, identifierOpts } = await this.getAuthParams({ ctx });
123
+ const customerId = args.customer_id || identifierOpts!.customerId;
124
+ return wrapSdkCall(() =>
125
+ client.customers.get(args.project_id, customerId)
126
+ );
127
+ },
128
+ }),
129
+
130
+ listCustomers: actionGeneric({
131
+ args: ListCustomersArgs,
132
+ handler: async (ctx, args) => {
133
+ const { client } = await this.getAuthParams({ ctx });
134
+ return wrapSdkCall(() => client.customers.list(args.project_id));
135
+ },
136
+ }),
137
+
138
+ listSubscriptions: actionGeneric({
139
+ args: ListSubscriptionsArgs,
140
+ handler: async (ctx, args) => {
141
+ const { client } = await this.getAuthParams({ ctx });
142
+ return wrapSdkCall(() => client.subscriptions.list(args.project_id));
143
+ },
144
+ }),
145
+ };
146
+ }
147
+ }
148
+
149
+ export * from "./types.js";
package/src/types.ts ADDED
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Convex validators for Surpay action arguments
3
+ */
4
+ import { v, Infer } from "convex/values";
5
+
6
+ // CreateCheckoutRequest: product_id, price_id, success_url, cancel_url (all required)
7
+ export const CreateCheckoutArgs = v.object({
8
+ product_id: v.string(),
9
+ price_id: v.string(),
10
+ success_url: v.string(),
11
+ cancel_url: v.string(),
12
+ });
13
+ export type CreateCheckoutArgs = Infer<typeof CreateCheckoutArgs>;
14
+
15
+ // ListSubscriptions: requires project_id
16
+ export const ListSubscriptionsArgs = v.object({
17
+ project_id: v.string(),
18
+ });
19
+ export type ListSubscriptionsArgs = Infer<typeof ListSubscriptionsArgs>;
20
+
21
+ // GetCustomer: requires project_id and customer_id
22
+ export const GetCustomerArgs = v.object({
23
+ project_id: v.string(),
24
+ customer_id: v.string(),
25
+ });
26
+ export type GetCustomerArgs = Infer<typeof GetCustomerArgs>;
27
+
28
+ // ListCustomers: requires project_id
29
+ export const ListCustomersArgs = v.object({
30
+ project_id: v.string(),
31
+ });
32
+ export type ListCustomersArgs = Infer<typeof ListCustomersArgs>;
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "rootDir": "src"
6
+ },
7
+ "include": ["src"],
8
+ "exclude": ["node_modules", "dist"]
9
+ }