@pulgueta/epayco-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.
Files changed (169) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +945 -0
  3. package/dist/client/_generated/_ignore.d.ts +1 -0
  4. package/dist/client/_generated/_ignore.d.ts.map +1 -0
  5. package/dist/client/_generated/_ignore.js +3 -0
  6. package/dist/client/_generated/_ignore.js.map +1 -0
  7. package/dist/client/index.d.ts +222 -0
  8. package/dist/client/index.d.ts.map +1 -0
  9. package/dist/client/index.js +355 -0
  10. package/dist/client/index.js.map +1 -0
  11. package/dist/component/_generated/api.d.ts +78 -0
  12. package/dist/component/_generated/api.d.ts.map +1 -0
  13. package/dist/component/_generated/api.js +31 -0
  14. package/dist/component/_generated/api.js.map +1 -0
  15. package/dist/component/_generated/component.d.ts +580 -0
  16. package/dist/component/_generated/component.d.ts.map +1 -0
  17. package/dist/component/_generated/component.js +11 -0
  18. package/dist/component/_generated/component.js.map +1 -0
  19. package/dist/component/_generated/dataModel.d.ts +46 -0
  20. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  21. package/dist/component/_generated/dataModel.js +11 -0
  22. package/dist/component/_generated/dataModel.js.map +1 -0
  23. package/dist/component/_generated/server.d.ts +121 -0
  24. package/dist/component/_generated/server.d.ts.map +1 -0
  25. package/dist/component/_generated/server.js +78 -0
  26. package/dist/component/_generated/server.js.map +1 -0
  27. package/dist/component/banks.d.ts +14 -0
  28. package/dist/component/banks.d.ts.map +1 -0
  29. package/dist/component/banks.js +37 -0
  30. package/dist/component/banks.js.map +1 -0
  31. package/dist/component/cashApi.d.ts +59 -0
  32. package/dist/component/cashApi.d.ts.map +1 -0
  33. package/dist/component/cashApi.js +88 -0
  34. package/dist/component/cashApi.js.map +1 -0
  35. package/dist/component/chargesApi.d.ts +64 -0
  36. package/dist/component/chargesApi.d.ts.map +1 -0
  37. package/dist/component/chargesApi.js +106 -0
  38. package/dist/component/chargesApi.js.map +1 -0
  39. package/dist/component/convex.config.d.ts +3 -0
  40. package/dist/component/convex.config.d.ts.map +1 -0
  41. package/dist/component/convex.config.js +6 -0
  42. package/dist/component/convex.config.js.map +1 -0
  43. package/dist/component/customers.d.ts +67 -0
  44. package/dist/component/customers.d.ts.map +1 -0
  45. package/dist/component/customers.js +103 -0
  46. package/dist/component/customers.js.map +1 -0
  47. package/dist/component/customersApi.d.ts +99 -0
  48. package/dist/component/customersApi.d.ts.map +1 -0
  49. package/dist/component/customersApi.js +176 -0
  50. package/dist/component/customersApi.js.map +1 -0
  51. package/dist/component/daviplataApi.d.ts +43 -0
  52. package/dist/component/daviplataApi.d.ts.map +1 -0
  53. package/dist/component/daviplataApi.js +103 -0
  54. package/dist/component/daviplataApi.js.map +1 -0
  55. package/dist/component/epaycoClient.d.ts +84 -0
  56. package/dist/component/epaycoClient.d.ts.map +1 -0
  57. package/dist/component/epaycoClient.js +422 -0
  58. package/dist/component/epaycoClient.js.map +1 -0
  59. package/dist/component/payloads.d.ts +34 -0
  60. package/dist/component/payloads.d.ts.map +1 -0
  61. package/dist/component/payloads.js +45 -0
  62. package/dist/component/payloads.js.map +1 -0
  63. package/dist/component/plans.d.ts +47 -0
  64. package/dist/component/plans.d.ts.map +1 -0
  65. package/dist/component/plans.js +83 -0
  66. package/dist/component/plans.js.map +1 -0
  67. package/dist/component/plansApi.d.ts +64 -0
  68. package/dist/component/plansApi.d.ts.map +1 -0
  69. package/dist/component/plansApi.js +121 -0
  70. package/dist/component/plansApi.js.map +1 -0
  71. package/dist/component/pseApi.d.ts +68 -0
  72. package/dist/component/pseApi.d.ts.map +1 -0
  73. package/dist/component/pseApi.js +113 -0
  74. package/dist/component/pseApi.js.map +1 -0
  75. package/dist/component/rateLimits.d.ts +69 -0
  76. package/dist/component/rateLimits.d.ts.map +1 -0
  77. package/dist/component/rateLimits.js +67 -0
  78. package/dist/component/rateLimits.js.map +1 -0
  79. package/dist/component/safetypayApi.d.ts +35 -0
  80. package/dist/component/safetypayApi.d.ts.map +1 -0
  81. package/dist/component/safetypayApi.js +68 -0
  82. package/dist/component/safetypayApi.js.map +1 -0
  83. package/dist/component/schema.d.ts +200 -0
  84. package/dist/component/schema.d.ts.map +1 -0
  85. package/dist/component/schema.js +104 -0
  86. package/dist/component/schema.js.map +1 -0
  87. package/dist/component/signature.d.ts +11 -0
  88. package/dist/component/signature.d.ts.map +1 -0
  89. package/dist/component/signature.js +28 -0
  90. package/dist/component/signature.js.map +1 -0
  91. package/dist/component/status.d.ts +12 -0
  92. package/dist/component/status.d.ts.map +1 -0
  93. package/dist/component/status.js +55 -0
  94. package/dist/component/status.js.map +1 -0
  95. package/dist/component/subscriptions.d.ts +69 -0
  96. package/dist/component/subscriptions.d.ts.map +1 -0
  97. package/dist/component/subscriptions.js +114 -0
  98. package/dist/component/subscriptions.js.map +1 -0
  99. package/dist/component/subscriptionsApi.d.ts +62 -0
  100. package/dist/component/subscriptionsApi.d.ts.map +1 -0
  101. package/dist/component/subscriptionsApi.js +147 -0
  102. package/dist/component/subscriptionsApi.js.map +1 -0
  103. package/dist/component/tokens.d.ts +31 -0
  104. package/dist/component/tokens.d.ts.map +1 -0
  105. package/dist/component/tokens.js +79 -0
  106. package/dist/component/tokens.js.map +1 -0
  107. package/dist/component/tokensApi.d.ts +18 -0
  108. package/dist/component/tokensApi.d.ts.map +1 -0
  109. package/dist/component/tokensApi.js +53 -0
  110. package/dist/component/tokensApi.js.map +1 -0
  111. package/dist/component/transactions.d.ts +103 -0
  112. package/dist/component/transactions.d.ts.map +1 -0
  113. package/dist/component/transactions.js +177 -0
  114. package/dist/component/transactions.js.map +1 -0
  115. package/dist/component/validators.d.ts +571 -0
  116. package/dist/component/validators.d.ts.map +1 -0
  117. package/dist/component/validators.js +203 -0
  118. package/dist/component/validators.js.map +1 -0
  119. package/dist/component/webhooks.d.ts +55 -0
  120. package/dist/component/webhooks.d.ts.map +1 -0
  121. package/dist/component/webhooks.js +172 -0
  122. package/dist/component/webhooks.js.map +1 -0
  123. package/dist/react/index.d.ts +16 -0
  124. package/dist/react/index.d.ts.map +1 -0
  125. package/dist/react/index.js +43 -0
  126. package/dist/react/index.js.map +1 -0
  127. package/package.json +106 -0
  128. package/src/client/_generated/_ignore.ts +1 -0
  129. package/src/client/index.test.ts +66 -0
  130. package/src/client/index.ts +633 -0
  131. package/src/client/setup.test.ts +26 -0
  132. package/src/component/_generated/api.ts +94 -0
  133. package/src/component/_generated/component.ts +809 -0
  134. package/src/component/_generated/dataModel.ts +60 -0
  135. package/src/component/_generated/server.ts +156 -0
  136. package/src/component/banks.ts +41 -0
  137. package/src/component/cashApi.ts +100 -0
  138. package/src/component/chargesApi.ts +119 -0
  139. package/src/component/convex.config.ts +7 -0
  140. package/src/component/customers.test.ts +122 -0
  141. package/src/component/customers.ts +116 -0
  142. package/src/component/customersApi.ts +206 -0
  143. package/src/component/daviplataApi.ts +119 -0
  144. package/src/component/epaycoApi.test.ts +110 -0
  145. package/src/component/epaycoClient.ts +578 -0
  146. package/src/component/payloads.ts +67 -0
  147. package/src/component/plans.test.ts +129 -0
  148. package/src/component/plans.ts +86 -0
  149. package/src/component/plansApi.ts +135 -0
  150. package/src/component/pseApi.ts +125 -0
  151. package/src/component/rateLimits.ts +67 -0
  152. package/src/component/safetypayApi.ts +78 -0
  153. package/src/component/schema.ts +124 -0
  154. package/src/component/setup.test.helper.ts +10 -0
  155. package/src/component/setup.test.ts +22 -0
  156. package/src/component/signature.ts +38 -0
  157. package/src/component/status.ts +71 -0
  158. package/src/component/subscriptions.test.ts +117 -0
  159. package/src/component/subscriptions.ts +128 -0
  160. package/src/component/subscriptionsApi.ts +172 -0
  161. package/src/component/tokens.ts +89 -0
  162. package/src/component/tokensApi.ts +63 -0
  163. package/src/component/transactions.test.ts +227 -0
  164. package/src/component/transactions.ts +200 -0
  165. package/src/component/validators.ts +245 -0
  166. package/src/component/webhooks.test.ts +137 -0
  167. package/src/component/webhooks.ts +229 -0
  168. package/src/react/index.ts +71 -0
  169. package/src/test.ts +13 -0
@@ -0,0 +1,53 @@
1
+ import { v } from "convex/values";
2
+ import { action } from "./_generated/server.js";
3
+ import { internal } from "./_generated/api.js";
4
+ import { epaycoCredentialsValidator, tokenInfoValidator } from "./validators.js";
5
+ import { getEpaycoClient, unwrap, dataOf, pick } from "./epaycoClient.js";
6
+ import { rateLimiter } from "./rateLimits.js";
7
+ /** Tokenize a card via ePayco and cache the resulting token locally. */
8
+ export const createToken = action({
9
+ args: {
10
+ credentials: epaycoCredentialsValidator,
11
+ userId: v.string(),
12
+ tokenInfo: tokenInfoValidator,
13
+ },
14
+ returns: v.any(),
15
+ handler: async (ctx, args) => {
16
+ await rateLimiter.limit(ctx, "createToken", {
17
+ key: args.userId,
18
+ throws: true,
19
+ });
20
+ const epayco = getEpaycoClient(args.credentials);
21
+ const result = unwrap(await epayco.token.create({
22
+ "card[number]": args.tokenInfo.cardNumber,
23
+ "card[exp_year]": args.tokenInfo.expYear,
24
+ "card[exp_month]": args.tokenInfo.expMonth,
25
+ "card[cvc]": args.tokenInfo.cvc,
26
+ hasCvv: args.tokenInfo.hasCvv ?? true,
27
+ }));
28
+ const data = dataOf(result);
29
+ // ePayco returns the card details (mask, franchise name) under `result.card`.
30
+ const card = typeof result.card === "object" && result.card !== null
31
+ ? result.card
32
+ : {};
33
+ const tokenId = pick(data, ["id", "token", "tokenId"]) ??
34
+ pick(result, ["id", "token", "tokenId"]);
35
+ if (tokenId) {
36
+ await ctx.runMutation(internal.tokens.upsertToken, {
37
+ userId: args.userId,
38
+ epaycoTokenId: tokenId,
39
+ epaycoCustomerId: "",
40
+ mask: pick(data, ["mask"]) ??
41
+ pick(card, ["mask"]) ??
42
+ `****${args.tokenInfo.cardNumber.slice(-4)}`,
43
+ franchise: pick(data, ["franchise"]) ??
44
+ pick(card, ["name", "franchise"]) ??
45
+ "unknown",
46
+ isActive: true,
47
+ lastSyncedAt: Date.now(),
48
+ });
49
+ }
50
+ return result;
51
+ },
52
+ });
53
+ //# sourceMappingURL=tokensApi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokensApi.js","sourceRoot":"","sources":["../../src/component/tokensApi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,0BAA0B,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACjF,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,wEAAwE;AACxE,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC;IAChC,IAAI,EAAE;QACJ,WAAW,EAAE,0BAA0B;QACvC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;QAClB,SAAS,EAAE,kBAAkB;KAC9B;IACD,OAAO,EAAE,CAAC,CAAC,GAAG,EAAE;IAChB,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAC3B,MAAM,WAAW,CAAC,KAAK,CAAC,GAAG,EAAE,aAAa,EAAE;YAC1C,GAAG,EAAE,IAAI,CAAC,MAAM;YAChB,MAAM,EAAE,IAAI;SACb,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACjD,MAAM,MAAM,GAAG,MAAM,CACnB,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;YACxB,cAAc,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU;YACzC,gBAAgB,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO;YACxC,iBAAiB,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ;YAC1C,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG;YAC/B,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,IAAI;SACtC,CAAC,CACH,CAAC;QAEF,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;QAC5B,8EAA8E;QAC9E,MAAM,IAAI,GACR,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI;YACrD,CAAC,CAAE,MAAM,CAAC,IAAgC;YAC1C,CAAC,CAAC,EAAE,CAAC;QACT,MAAM,OAAO,GACX,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;YACtC,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC;QAE3C,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,GAAG,CAAC,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE;gBACjD,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,aAAa,EAAE,OAAO;gBACtB,gBAAgB,EAAE,EAAE;gBACpB,IAAI,EACF,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC;oBACpB,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC;oBACpB,OAAO,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE;gBAC9C,SAAS,EACP,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,CAAC,CAAC;oBACzB,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;oBACjC,SAAS;gBACX,QAAQ,EAAE,IAAI;gBACd,YAAY,EAAE,IAAI,CAAC,GAAG,EAAE;aACzB,CAAC,CAAC;QACL,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,103 @@
1
+ export declare const getLocalTransaction: import("convex/server").RegisteredQuery<"public", {
2
+ epaycoRef: string;
3
+ }, Promise<{
4
+ _id: import("convex/values").GenericId<"transactions">;
5
+ _creationTime: number;
6
+ franchise?: string | undefined;
7
+ splitReceivers?: {
8
+ fee?: number | undefined;
9
+ id: string;
10
+ total: number;
11
+ iva: number;
12
+ base_iva: number;
13
+ }[] | undefined;
14
+ epaycoTransactionId?: string | undefined;
15
+ customerEmail?: string | undefined;
16
+ bankName?: string | undefined;
17
+ cashProvider?: string | undefined;
18
+ responseCode?: string | undefined;
19
+ responseMessage?: string | undefined;
20
+ splitPayment?: boolean | undefined;
21
+ rawResponse?: any;
22
+ userId: string;
23
+ epaycoRef: string;
24
+ status: string;
25
+ amount: number;
26
+ currency: string;
27
+ description: string;
28
+ paymentMethod: "credit_card" | "pse" | "cash" | "daviplata" | "safetypay";
29
+ lastSyncedAt: number;
30
+ } | null>>;
31
+ export declare const listLocalTransactions: import("convex/server").RegisteredQuery<"public", {
32
+ limit?: number | undefined;
33
+ status?: string | undefined;
34
+ paymentMethod?: "credit_card" | "pse" | "cash" | "daviplata" | "safetypay" | undefined;
35
+ userId: string;
36
+ }, Promise<{
37
+ _id: import("convex/values").GenericId<"transactions">;
38
+ _creationTime: number;
39
+ franchise?: string | undefined;
40
+ splitReceivers?: {
41
+ fee?: number | undefined;
42
+ id: string;
43
+ total: number;
44
+ iva: number;
45
+ base_iva: number;
46
+ }[] | undefined;
47
+ epaycoTransactionId?: string | undefined;
48
+ customerEmail?: string | undefined;
49
+ bankName?: string | undefined;
50
+ cashProvider?: string | undefined;
51
+ responseCode?: string | undefined;
52
+ responseMessage?: string | undefined;
53
+ splitPayment?: boolean | undefined;
54
+ rawResponse?: any;
55
+ userId: string;
56
+ epaycoRef: string;
57
+ status: string;
58
+ amount: number;
59
+ currency: string;
60
+ description: string;
61
+ paymentMethod: "credit_card" | "pse" | "cash" | "daviplata" | "safetypay";
62
+ lastSyncedAt: number;
63
+ }[]>>;
64
+ export declare const upsertTransaction: import("convex/server").RegisteredMutation<"internal", {
65
+ franchise?: string | undefined;
66
+ splitReceivers?: {
67
+ fee?: number | undefined;
68
+ id: string;
69
+ total: number;
70
+ iva: number;
71
+ base_iva: number;
72
+ }[] | undefined;
73
+ epaycoTransactionId?: string | undefined;
74
+ customerEmail?: string | undefined;
75
+ bankName?: string | undefined;
76
+ cashProvider?: string | undefined;
77
+ responseCode?: string | undefined;
78
+ responseMessage?: string | undefined;
79
+ splitPayment?: boolean | undefined;
80
+ rawResponse?: any;
81
+ userId: string;
82
+ epaycoRef: string;
83
+ status: string;
84
+ amount: number;
85
+ currency: string;
86
+ description: string;
87
+ paymentMethod: "credit_card" | "pse" | "cash" | "daviplata" | "safetypay";
88
+ lastSyncedAt: number;
89
+ }, Promise<import("convex/values").GenericId<"transactions">>>;
90
+ /**
91
+ * Patch the status of an existing transaction. Returns `true` when a row was
92
+ * found and updated, `false` when no local row exists yet — so callers (e.g. the
93
+ * webhook handler) can detect a confirmation that arrived before local
94
+ * persistence instead of silently dropping it.
95
+ */
96
+ export declare const updateTransactionStatus: import("convex/server").RegisteredMutation<"internal", {
97
+ responseCode?: string | undefined;
98
+ responseMessage?: string | undefined;
99
+ rawResponse?: any;
100
+ epaycoRef: string;
101
+ status: string;
102
+ }, Promise<boolean>>;
103
+ //# sourceMappingURL=transactions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transactions.d.ts","sourceRoot":"","sources":["../../src/component/transactions.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAS9B,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA8BhC,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;8DA+E5B,CAAC;AAwCH;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB;;;;;;oBA0BlC,CAAC"}
@@ -0,0 +1,177 @@
1
+ import { v } from "convex/values";
2
+ import { internalMutation, query } from "./_generated/server.js";
3
+ import { paymentMethodValidator } from "./validators.js";
4
+ import { statusFromCodResponse } from "./status.js";
5
+ export const getLocalTransaction = query({
6
+ args: { epaycoRef: v.string() },
7
+ returns: v.any(),
8
+ handler: async (ctx, args) => {
9
+ return await ctx.db
10
+ .query("transactions")
11
+ .withIndex("by_epaycoRef", (q) => q.eq("epaycoRef", args.epaycoRef))
12
+ .first();
13
+ },
14
+ });
15
+ export const listLocalTransactions = query({
16
+ args: {
17
+ userId: v.string(),
18
+ status: v.optional(v.string()),
19
+ paymentMethod: v.optional(paymentMethodValidator),
20
+ limit: v.optional(v.number()),
21
+ },
22
+ returns: v.any(),
23
+ handler: async (ctx, args) => {
24
+ let q;
25
+ if (args.status) {
26
+ q = ctx.db
27
+ .query("transactions")
28
+ .withIndex("by_userId_and_status", (q) => q.eq("userId", args.userId).eq("status", args.status));
29
+ }
30
+ else {
31
+ q = ctx.db
32
+ .query("transactions")
33
+ .withIndex("by_userId", (q) => q.eq("userId", args.userId));
34
+ }
35
+ if (args.paymentMethod) {
36
+ q = q.filter((q) => q.eq(q.field("paymentMethod"), args.paymentMethod));
37
+ }
38
+ return await q.order("desc").take(args.limit ?? 100);
39
+ },
40
+ });
41
+ export const upsertTransaction = internalMutation({
42
+ args: {
43
+ userId: v.string(),
44
+ epaycoRef: v.string(),
45
+ epaycoTransactionId: v.optional(v.string()),
46
+ paymentMethod: paymentMethodValidator,
47
+ status: v.string(),
48
+ amount: v.number(),
49
+ currency: v.string(),
50
+ description: v.string(),
51
+ customerEmail: v.optional(v.string()),
52
+ bankName: v.optional(v.string()),
53
+ cashProvider: v.optional(v.string()),
54
+ responseCode: v.optional(v.string()),
55
+ responseMessage: v.optional(v.string()),
56
+ franchise: v.optional(v.string()),
57
+ splitPayment: v.optional(v.boolean()),
58
+ splitReceivers: v.optional(v.array(v.object({
59
+ id: v.string(),
60
+ total: v.number(),
61
+ iva: v.number(),
62
+ base_iva: v.number(),
63
+ fee: v.optional(v.number()),
64
+ }))),
65
+ rawResponse: v.optional(v.any()),
66
+ lastSyncedAt: v.number(),
67
+ },
68
+ handler: async (ctx, args) => {
69
+ const existing = await ctx.db
70
+ .query("transactions")
71
+ .withIndex("by_epaycoRef", (q) => q.eq("epaycoRef", args.epaycoRef))
72
+ .first();
73
+ if (existing) {
74
+ if (existing.lastSyncedAt >= args.lastSyncedAt)
75
+ return existing._id;
76
+ await ctx.db.patch(existing._id, {
77
+ status: args.status,
78
+ responseCode: args.responseCode,
79
+ responseMessage: args.responseMessage,
80
+ rawResponse: args.rawResponse,
81
+ lastSyncedAt: args.lastSyncedAt,
82
+ });
83
+ return existing._id;
84
+ }
85
+ const insertedId = await ctx.db.insert("transactions", {
86
+ userId: args.userId,
87
+ epaycoRef: args.epaycoRef,
88
+ epaycoTransactionId: args.epaycoTransactionId,
89
+ paymentMethod: args.paymentMethod,
90
+ status: args.status,
91
+ amount: args.amount,
92
+ currency: args.currency,
93
+ description: args.description,
94
+ customerEmail: args.customerEmail,
95
+ bankName: args.bankName,
96
+ cashProvider: args.cashProvider,
97
+ responseCode: args.responseCode,
98
+ responseMessage: args.responseMessage,
99
+ franchise: args.franchise,
100
+ splitPayment: args.splitPayment,
101
+ splitReceivers: args.splitReceivers,
102
+ rawResponse: args.rawResponse,
103
+ lastSyncedAt: args.lastSyncedAt,
104
+ });
105
+ // Reconcile a confirmation that arrived BEFORE this row existed: an async
106
+ // PSE/cash/Daviplata webhook can race ahead of (or be independent of) local
107
+ // persistence, in which case `webhooks.processConfirmation` verified it and
108
+ // parked the event as `pending`. Apply it now so the status is never lost.
109
+ await drainPendingConfirmation(ctx, args.epaycoRef, insertedId);
110
+ return insertedId;
111
+ },
112
+ });
113
+ /**
114
+ * If a verified-but-unapplied confirmation event is waiting for `epaycoRef`,
115
+ * apply its outcome to the (now-existing) transaction row and mark the event
116
+ * processed. Deterministic, so no cron/scheduler is needed; it runs whenever a
117
+ * transaction is first inserted. Safe no-op when nothing is waiting.
118
+ */
119
+ async function drainPendingConfirmation(ctx, epaycoRef, transactionId) {
120
+ const event = await ctx.db
121
+ .query("webhookEvents")
122
+ .withIndex("by_epaycoRef", (q) => q.eq("epaycoRef", epaycoRef))
123
+ .order("desc")
124
+ .first();
125
+ if (!event || event.status !== "pending")
126
+ return;
127
+ const payload = (event.rawPayload ?? {});
128
+ const cod = payload.x_cod_response;
129
+ if (cod === undefined || cod === null)
130
+ return;
131
+ const reason = payload.x_response_reason_text ?? payload.x_response;
132
+ await ctx.db.patch(transactionId, {
133
+ status: statusFromCodResponse(String(cod)),
134
+ responseCode: String(cod),
135
+ responseMessage: reason === undefined || reason === null ? undefined : String(reason),
136
+ rawResponse: payload,
137
+ lastSyncedAt: Date.now(),
138
+ });
139
+ await ctx.db.patch(event._id, {
140
+ status: "processed",
141
+ processedAt: Date.now(),
142
+ lastSyncedAt: Date.now(),
143
+ });
144
+ }
145
+ /**
146
+ * Patch the status of an existing transaction. Returns `true` when a row was
147
+ * found and updated, `false` when no local row exists yet — so callers (e.g. the
148
+ * webhook handler) can detect a confirmation that arrived before local
149
+ * persistence instead of silently dropping it.
150
+ */
151
+ export const updateTransactionStatus = internalMutation({
152
+ args: {
153
+ epaycoRef: v.string(),
154
+ status: v.string(),
155
+ responseCode: v.optional(v.string()),
156
+ responseMessage: v.optional(v.string()),
157
+ rawResponse: v.optional(v.any()),
158
+ },
159
+ returns: v.boolean(),
160
+ handler: async (ctx, args) => {
161
+ const existing = await ctx.db
162
+ .query("transactions")
163
+ .withIndex("by_epaycoRef", (q) => q.eq("epaycoRef", args.epaycoRef))
164
+ .first();
165
+ if (!existing)
166
+ return false;
167
+ await ctx.db.patch(existing._id, {
168
+ status: args.status,
169
+ responseCode: args.responseCode,
170
+ responseMessage: args.responseMessage,
171
+ rawResponse: args.rawResponse,
172
+ lastSyncedAt: Date.now(),
173
+ });
174
+ return true;
175
+ },
176
+ });
177
+ //# sourceMappingURL=transactions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transactions.js","sourceRoot":"","sources":["../../src/component/transactions.ts"],"names":[],"mappings":"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"}