@social-mail/social-mail-web-server 1.8.495 → 1.8.499

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 (120) hide show
  1. package/dist/server/model/SocialMailContext.d.ts +8 -0
  2. package/dist/server/model/SocialMailContext.d.ts.map +1 -1
  3. package/dist/server/model/SocialMailContext.js +8 -0
  4. package/dist/server/model/SocialMailContext.js.map +1 -1
  5. package/dist/server/model/SocialMailContextEvents.d.ts.map +1 -1
  6. package/dist/server/model/SocialMailContextEvents.js +9 -0
  7. package/dist/server/model/SocialMailContextEvents.js.map +1 -1
  8. package/dist/server/model/entities/HostedDomain.d.ts +2 -0
  9. package/dist/server/model/entities/HostedDomain.d.ts.map +1 -1
  10. package/dist/server/model/entities/HostedDomain.js.map +1 -1
  11. package/dist/server/model/entities/StoreAccount.d.ts +2 -0
  12. package/dist/server/model/entities/StoreAccount.d.ts.map +1 -1
  13. package/dist/server/model/entities/StoreAccount.js.map +1 -1
  14. package/dist/server/model/entities/StoreItem.d.ts +1 -1
  15. package/dist/server/model/entities/StoreItem.d.ts.map +1 -1
  16. package/dist/server/model/entities/StoreItem.js +1 -1
  17. package/dist/server/model/entities/StoreItem.js.map +1 -1
  18. package/dist/server/model/entities/StoreItemPrice.d.ts +10 -0
  19. package/dist/server/model/entities/StoreItemPrice.d.ts.map +1 -1
  20. package/dist/server/model/entities/StoreItemPrice.js +14 -0
  21. package/dist/server/model/entities/StoreItemPrice.js.map +1 -1
  22. package/dist/server/model/entities/StorePGItemPrice.d.ts +8 -0
  23. package/dist/server/model/entities/StorePGItemPrice.d.ts.map +1 -0
  24. package/dist/server/model/entities/StorePGItemPrice.js +36 -0
  25. package/dist/server/model/entities/StorePGItemPrice.js.map +1 -0
  26. package/dist/server/model/entities/StorePGSubscription.d.ts +13 -0
  27. package/dist/server/model/entities/StorePGSubscription.d.ts.map +1 -0
  28. package/dist/server/model/entities/StorePGSubscription.js +62 -0
  29. package/dist/server/model/entities/StorePGSubscription.js.map +1 -0
  30. package/dist/server/model/entities/StorePaymentGateway.d.ts +5 -1
  31. package/dist/server/model/entities/StorePaymentGateway.d.ts.map +1 -1
  32. package/dist/server/model/entities/StorePaymentGateway.js +1 -1
  33. package/dist/server/model/entities/StorePaymentGateway.js.map +1 -1
  34. package/dist/server/model/entities/StoreSubscription.d.ts +36 -0
  35. package/dist/server/model/entities/StoreSubscription.d.ts.map +1 -0
  36. package/dist/server/model/entities/StoreSubscription.js +81 -0
  37. package/dist/server/model/entities/StoreSubscription.js.map +1 -0
  38. package/dist/server/model/entities/WebSite.d.ts +4 -0
  39. package/dist/server/model/entities/WebSite.d.ts.map +1 -1
  40. package/dist/server/model/entities/WebSite.js.map +1 -1
  41. package/dist/server/model/entities/WebSiteSubscription.d.ts +12 -0
  42. package/dist/server/model/entities/WebSiteSubscription.d.ts.map +1 -0
  43. package/dist/server/model/entities/WebSiteSubscription.js +54 -0
  44. package/dist/server/model/entities/WebSiteSubscription.js.map +1 -0
  45. package/dist/server/model/events/StorePGItemPriceEvents.d.ts +5 -0
  46. package/dist/server/model/events/StorePGItemPriceEvents.d.ts.map +1 -0
  47. package/dist/server/model/events/StorePGItemPriceEvents.js +4 -0
  48. package/dist/server/model/events/StorePGItemPriceEvents.js.map +1 -0
  49. package/dist/server/model/events/StorePGSubscriptionEvents.d.ts +7 -0
  50. package/dist/server/model/events/StorePGSubscriptionEvents.d.ts.map +1 -0
  51. package/dist/server/model/events/StorePGSubscriptionEvents.js +12 -0
  52. package/dist/server/model/events/StorePGSubscriptionEvents.js.map +1 -0
  53. package/dist/server/model/events/StoreSubscriptionEvents.d.ts +7 -0
  54. package/dist/server/model/events/StoreSubscriptionEvents.d.ts.map +1 -0
  55. package/dist/server/model/events/StoreSubscriptionEvents.js +12 -0
  56. package/dist/server/model/events/StoreSubscriptionEvents.js.map +1 -0
  57. package/dist/server/model/events/VerificationTokenEvents.js +2 -2
  58. package/dist/server/model/events/VerificationTokenEvents.js.map +1 -1
  59. package/dist/server/seed/ui/seed-ui.js +1 -1
  60. package/dist/server/services/store/LedgerPaymentGateway.d.ts +10 -0
  61. package/dist/server/services/store/LedgerPaymentGateway.d.ts.map +1 -1
  62. package/dist/server/services/store/LedgerPaymentGateway.js.map +1 -1
  63. package/dist/server/services/store/StorePaymentGatewayService.d.ts +16 -0
  64. package/dist/server/services/store/StorePaymentGatewayService.d.ts.map +1 -1
  65. package/dist/server/services/store/StorePaymentGatewayService.js +30 -0
  66. package/dist/server/services/store/StorePaymentGatewayService.js.map +1 -1
  67. package/dist/server/services/store/gateways/cashfree.d.ts +32 -0
  68. package/dist/server/services/store/gateways/cashfree.d.ts.map +1 -0
  69. package/dist/server/services/store/gateways/cashfree.js +192 -0
  70. package/dist/server/services/store/gateways/cashfree.js.map +1 -0
  71. package/dist/server/services/store/gateways/paypal.d.ts +6 -0
  72. package/dist/server/services/store/gateways/paypal.d.ts.map +1 -1
  73. package/dist/server/services/store/gateways/paypal.js +53 -1
  74. package/dist/server/services/store/gateways/paypal.js.map +1 -1
  75. package/dist/server/services/store/gateways/phone-pe.d.ts +4 -0
  76. package/dist/server/services/store/gateways/phone-pe.d.ts.map +1 -1
  77. package/dist/server/services/store/gateways/phone-pe.js +6 -0
  78. package/dist/server/services/store/gateways/phone-pe.js.map +1 -1
  79. package/dist/server/services/store/gateways/stripe.d.ts +4 -0
  80. package/dist/server/services/store/gateways/stripe.d.ts.map +1 -1
  81. package/dist/server/services/store/gateways/stripe.js +6 -0
  82. package/dist/server/services/store/gateways/stripe.js.map +1 -1
  83. package/dist/tsconfig.tsbuildinfo +1 -1
  84. package/dist/wwwroot/routes/store/buy/[productID]/get.js +1 -1
  85. package/dist/wwwroot/routes/store/buy/[productID]/get.js.map +1 -1
  86. package/dist/wwwroot/routes/store/subscribe/[productID]/get.d.ts +14 -0
  87. package/dist/wwwroot/routes/store/subscribe/[productID]/get.d.ts.map +1 -0
  88. package/dist/wwwroot/routes/store/subscribe/[productID]/get.js +80 -0
  89. package/dist/wwwroot/routes/store/subscribe/[productID]/get.js.map +1 -0
  90. package/dist/wwwroot/routes/store/subscribe-callback/[subscriptionID]/get.d.ts +10 -0
  91. package/dist/wwwroot/routes/store/subscribe-callback/[subscriptionID]/get.d.ts.map +1 -0
  92. package/dist/wwwroot/routes/store/subscribe-callback/[subscriptionID]/get.js +38 -0
  93. package/dist/wwwroot/routes/store/subscribe-callback/[subscriptionID]/get.js.map +1 -0
  94. package/package.json +3 -2
  95. package/src/server/model/SocialMailContext.ts +12 -0
  96. package/src/server/model/SocialMailContextEvents.ts +10 -0
  97. package/src/server/model/entities/HostedDomain.ts +2 -0
  98. package/src/server/model/entities/StoreAccount.ts +2 -0
  99. package/src/server/model/entities/StoreItem.ts +1 -1
  100. package/src/server/model/entities/StoreItemPrice.ts +19 -0
  101. package/src/server/model/entities/StorePGItemPrice.ts +24 -0
  102. package/src/server/model/entities/StorePGSubscription.ts +48 -0
  103. package/src/server/model/entities/StorePaymentGateway.ts +5 -1
  104. package/src/server/model/entities/StoreSubscription.ts +75 -0
  105. package/src/server/model/entities/WebSite.ts +4 -0
  106. package/src/server/model/entities/WebSiteSubscription.ts +44 -0
  107. package/src/server/model/events/StorePGItemPriceEvents.ts +5 -0
  108. package/src/server/model/events/StorePGSubscriptionEvents.ts +17 -0
  109. package/src/server/model/events/StoreSubscriptionEvents.ts +16 -0
  110. package/src/server/model/events/VerificationTokenEvents.ts +2 -2
  111. package/src/server/seed/ui/seed-ui.ts +1 -1
  112. package/src/server/services/store/LedgerPaymentGateway.ts +6 -0
  113. package/src/server/services/store/StorePaymentGatewayService.ts +42 -0
  114. package/src/server/services/store/gateways/cashfree.tsx +237 -0
  115. package/src/server/services/store/gateways/paypal.tsx +73 -2
  116. package/src/server/services/store/gateways/phone-pe.tsx +9 -0
  117. package/src/server/services/store/gateways/stripe.tsx +9 -0
  118. package/src/wwwroot/routes/store/buy/[productID]/get.tsx +1 -1
  119. package/src/wwwroot/routes/store/subscribe/[productID]/get.tsx +82 -0
  120. package/src/wwwroot/routes/store/subscribe-callback/[subscriptionID]/get.tsx +30 -0
@@ -153,8 +153,8 @@ export default class VerificationTokenEvents extends AuthenticatedEvents<Verific
153
153
  const { email } = entity;
154
154
  webSiteFolderID = w.folderID;
155
155
  const user = await db.users.where({ webSiteFolderID, email }, (p) => (x) =>
156
- x.contactEmail === p.email
157
- && x.storeAccounts.some((s) => s.storeID === p.webSiteFolderID)
156
+ x.storeAccounts.some((s) => s.storeID === p.webSiteFolderID
157
+ && s.emails.some((e) => e.emailAddress.emailAddress === p.email))
158
158
  )
159
159
  .first();
160
160
  if (user) {
@@ -17,7 +17,7 @@ export default async function seedUI(config: DBConfig) {
17
17
  await config.saveVersion(UIPackageConfig, {
18
18
  package: "@social-mail/social-mail-client",
19
19
  view: "dist/web/AppIndex",
20
- version: "1.9.121"
20
+ version: "1.9.126"
21
21
  });
22
22
 
23
23
  await config.saveVersion(WebComponentsPackageConfig, {
@@ -2,6 +2,8 @@ import { ServiceProvider } from "@entity-access/entity-access/dist/di/di.js";
2
2
  import { StoreJournal } from "../../model/entities/StoreJournal.js";
3
3
  import StorePaymentAttempt from "../../model/entities/StorePaymentAttempt.js";
4
4
  import StorePaymentGateway from "../../model/entities/StorePaymentGateway.js";
5
+ import StorePGSubscription from "../../model/entities/StorePGSubscription.js";
6
+ import StoreSubscription from "../../model/entities/StoreSubscription.js";
5
7
 
6
8
  export default abstract class LedgerPaymentGateway {
7
9
 
@@ -14,4 +16,8 @@ export default abstract class LedgerPaymentGateway {
14
16
 
15
17
  abstract isSuccessful(pa: StorePaymentAttempt);
16
18
 
19
+ abstract createSubscription(host: string, gateway: StorePaymentGateway, subscription: StoreSubscription): Promise<{ url?, content? }>;
20
+
21
+ abstract fetchSubscriptionInfo(pgSubscription: StorePGSubscription): Promise<{ active?, error? }>;
22
+
17
23
  }
@@ -5,6 +5,7 @@ import LedgerPaymentGateway from "./LedgerPaymentGateway.js";
5
5
  import XNode from "@entity-access/server-pages/dist/html/XNode.js";
6
6
  import QueueInvokeWebHookWorkflow from "../../workflows/web-hooks/QueueInvokeWebHookWorkflow.js";
7
7
  import { AppWorkflowContext } from "../../workflows/AppWorkflowContext.js";
8
+ import StoreSubscription from "../../model/entities/StoreSubscription.js";
8
9
 
9
10
  @RegisterScoped
10
11
  export default class StorePaymentGatewayService {
@@ -87,6 +88,46 @@ export default class StorePaymentGatewayService {
87
88
  return payment;
88
89
  }
89
90
 
91
+ async createSubscription({ storeID, subscription ,ledgerID, host, userID }: { storeID, ledgerID?, userID?, subscription: StoreSubscription, host: string}) {
92
+
93
+ let q = this.db.storePaymentGateways.where({ storeID}, (p) => (x) =>
94
+ x.storeAccount.storeID === p.storeID
95
+ );
96
+
97
+ if (ledgerID) {
98
+ q = q.where({ ledgerID }, (p) => (x) => x.accountID === p.ledgerID);
99
+ } else{
100
+ q = q.where(void 0, (p) => (x) => x.isGatewayActive === true);
101
+ }
102
+
103
+ const gateway = await q
104
+ .orderByDescending(void 0, (p) => (x) => x.isDefault)
105
+ .first();
106
+
107
+ const pg = await LedgerPaymentGateway.for(this, gateway.paymentGateway);
108
+ const r = await pg.createSubscription(host, gateway, subscription);
109
+ await this.db.saveChanges();
110
+ return r;
111
+ }
112
+
113
+ async fetchSubscription(subscriptionID) {
114
+ const subscription = await this.db.storePGSubscriptions
115
+ .where({ subscriptionID}, (p) => (x) => x.subscriptionID === p.subscriptionID && x.active === true)
116
+ .include((x) => [x.gateway, x.subscription.itemPrice])
117
+ .first();
118
+
119
+ const { gateway } = subscription;
120
+
121
+ const pg = await LedgerPaymentGateway.for(this, gateway.paymentGateway);
122
+
123
+ const r = await pg.fetchSubscriptionInfo(subscription);
124
+ if (r.active) {
125
+ subscription.active = true;
126
+ await this.db.storePGSubscriptions.statements.update({ active: true}, { subscriptionID, paymentGatewayID: gateway.accountID });
127
+ }
128
+ return { ... r, subscription };
129
+ }
130
+
90
131
  private async updatePaymentStatus(orderID: any) {
91
132
 
92
133
  const order = await this.db.storeJournals.where({ orderID }, (p) => (x) => x.journalID === p.orderID)
@@ -139,4 +180,5 @@ export default class StorePaymentGatewayService {
139
180
  // });
140
181
  }
141
182
  }
183
+
142
184
  }
@@ -0,0 +1,237 @@
1
+ import Inject, { RegisterSingleton } from "@entity-access/entity-access/dist/di/di.js";
2
+ import { StoreJournal } from "../../../model/entities/StoreJournal.js";
3
+ import StorePaymentAttempt from "../../../model/entities/StorePaymentAttempt.js";
4
+ import StorePaymentGateway from "../../../model/entities/StorePaymentGateway.js";
5
+ import EncryptionService from "../../encryption/EncryptionService.js";
6
+ import LedgerPaymentGateway from "../LedgerPaymentGateway.js";
7
+ import SocialMailContext from "../../../model/SocialMailContext.js";
8
+ import TimedCache from "@entity-access/entity-access/dist/common/cache/TimedCache.js";
9
+
10
+ import { Cashfree, CFEnvironment } from "cashfree-pg";
11
+ import HtmlDocument from "@entity-access/server-pages/dist/html/HtmlDocument.js";
12
+ import XNode from "@entity-access/server-pages/dist/html/XNode.js";
13
+ import StorePGSubscription from "../../../model/entities/StorePGSubscription.js";
14
+ import StoreSubscription from "../../../model/entities/StoreSubscription.js";
15
+ import EntityAccessError from "@entity-access/entity-access/dist/common/EntityAccessError.js";
16
+
17
+ @RegisterSingleton
18
+ class CashfreeClientCache {
19
+
20
+ cache = new TimedCache<any, Cashfree>();
21
+
22
+ @Inject
23
+ ecs: EncryptionService;
24
+
25
+ getClient(gateway: StorePaymentGateway) {
26
+ return this.cache.getOrCreate(gateway.accountID, gateway, (k, p) => {
27
+ const secret = this.ecs.secure.decrypt(p.encryptedSecret);
28
+
29
+ return new Cashfree(gateway.url ? CFEnvironment.SANDBOX : CFEnvironment.PRODUCTION,
30
+ gateway.clientID,
31
+ secret
32
+ );
33
+ });
34
+ }
35
+
36
+ }
37
+
38
+ export default class extends LedgerPaymentGateway {
39
+
40
+ @Inject
41
+ ecs: EncryptionService;
42
+
43
+ @Inject
44
+ pc: CashfreeClientCache;
45
+
46
+ @Inject
47
+ db: SocialMailContext;
48
+
49
+
50
+ async createPayment(host: string, gateway: StorePaymentGateway, order: StoreJournal, pa: StorePaymentAttempt) {
51
+
52
+ const pID = this.ecs.general.encrypt(pa.attemptID.toString());
53
+
54
+ const mode = gateway.url ? "sandbox" : "production";
55
+
56
+ const items = await this.db.storeJournalLineItems.where(order, (p) => (x) => x.journalID === p.journalID)
57
+ .include((x) => x.itemPrice.storeItem)
58
+ .toArray();
59
+
60
+ const client = this.pc.getClient(gateway);
61
+
62
+ const { emailAddress: { emailAddress: emailAddress} } = await this.db.storeAccountEmails
63
+ .where(order, (p) => (x) => x.accountID === p.buyerID)
64
+ .include((x) => x.emailAddress)
65
+ .first();
66
+
67
+ const currencyCode = items[0].itemPrice.currency;
68
+
69
+
70
+ const { phoneNumberE64 } = await this.db.storeAccountPhones
71
+ .where(order, (p) => (x) => x.accountID === p.buyerID)
72
+ .first();
73
+
74
+ const returnUrl = `https://${host}/store/checkout-callback/${pID}`;
75
+
76
+ const pgOrder = await client.PGCreateOrder({
77
+ order_amount: order.total,
78
+ order_currency: currencyCode,
79
+ order_meta: {
80
+ return_url: returnUrl
81
+ },
82
+ customer_details: {
83
+ customer_id: emailAddress,
84
+ customer_email: emailAddress,
85
+ customer_phone: phoneNumberE64,
86
+ }
87
+ });
88
+
89
+ const paymentSessionId = pgOrder.data.payment_session_id;
90
+ pa.externalID = pgOrder.data.order_id;
91
+ await this.db.storePaymentAttempts.statements.update({ externalID: pa.externalID }, { attemptID: pa.attemptID });
92
+
93
+ return {
94
+ content: <HtmlDocument>
95
+ <head>
96
+ <title>Creating Payment Request</title>
97
+ </head>
98
+ <body>
99
+ <p>
100
+ Creating Payment Request ...
101
+ </p>
102
+ <script src="https://sdk.cashfree.com/js/v3/cashfree.js"></script>
103
+ <script>
104
+ const cashfree = Cashfree({JSON.stringify({
105
+ mode,
106
+ })});
107
+ let checkoutOptions = {JSON.stringify({
108
+ paymentSessionId,
109
+ redirectTarget: "_top",
110
+ })};
111
+ cashfree.checkout(checkoutOptions);
112
+ </script>
113
+ </body>
114
+ </HtmlDocument>
115
+ };
116
+ }
117
+
118
+ async isSuccessful(pa: StorePaymentAttempt) {
119
+
120
+ const { gateway, externalID: id } = pa;
121
+
122
+ const client = this.pc.getClient(gateway);
123
+
124
+ const order = await client.PGFetchOrder(id);
125
+
126
+ if (/paid/i.test(order.data.order_status)) {
127
+ // let us capture...
128
+ return true;
129
+ }
130
+
131
+ return false;
132
+ }
133
+
134
+
135
+ async createSubscription(host: string, gateway: StorePaymentGateway, storeSubscription: StoreSubscription) {
136
+
137
+ try {
138
+ const client = this.pc.getClient(gateway);
139
+
140
+ const mode = gateway.url ? "sandbox" : "production";
141
+
142
+ const { emailAddress: { emailAddress: emailAddress} } = await this.db.storeAccountEmails
143
+ .where(storeSubscription, (p) => (x) => x.accountID === p.buyerID)
144
+ .include((x) => x.emailAddress)
145
+ .first();
146
+
147
+ const { phoneNumberE64 } = await this.db.storeAccountPhones
148
+ .where(storeSubscription, (p) => (x) => x.accountID === p.buyerID)
149
+ .first();
150
+
151
+ const pID = this.ecs.general.encrypt(storeSubscription.subscriptionID.toString());
152
+
153
+ const returnUrl = `https://${host}/store/subscribe-callback/${pID}`;
154
+
155
+ const { itemPrice } = storeSubscription;
156
+
157
+ let plan_interval_type = "MONTH";
158
+ if (/year/i.test(itemPrice.intervalType)) {
159
+ plan_interval_type = "YEAR";
160
+ }
161
+
162
+ const sub = await client.SubsCreateSubscription({
163
+ subscription_meta: {
164
+ return_url: returnUrl
165
+ },
166
+ plan_details: {
167
+ plan_name: itemPrice.priceID.toString() + ": " + itemPrice.storeItem.name,
168
+ plan_amount: Number(itemPrice.amount),
169
+ plan_interval_type,
170
+ plan_max_amount: Number(itemPrice.amount),
171
+ plan_currency: itemPrice.currency,
172
+ plan_type: "PERIODIC"
173
+ },
174
+ subscription_id: storeSubscription.subscriptionID.toString(),
175
+ customer_details: {
176
+ customer_email: emailAddress,
177
+ customer_phone: phoneNumberE64,
178
+ }
179
+ });
180
+
181
+ this.db.storePGSubscriptions.add({
182
+ gatewaySubscriptionID: sub.data.subscription_id,
183
+ active: true,
184
+ paymentGatewayID: gateway.accountID,
185
+ subscriptionID: storeSubscription.subscriptionID
186
+ });
187
+
188
+ await this.db.saveChanges();
189
+
190
+ const subsSessionId = sub.data.subscription_session_id;
191
+
192
+ return {
193
+ content: <HtmlDocument>
194
+ <head>
195
+ <title>Creating Payment Request</title>
196
+ </head>
197
+ <body>
198
+ <p>
199
+ Creating Payment Request ...
200
+ </p>
201
+ <script src="https://sdk.cashfree.com/js/v3/cashfree.js"></script>
202
+ <script>
203
+ const cashfree = Cashfree({JSON.stringify({
204
+ mode,
205
+ })});
206
+ let checkoutOptions = {JSON.stringify({
207
+ subsSessionId,
208
+ redirectTarget: "_top",
209
+ })};
210
+ cashfree.subscriptionsCheckout(checkoutOptions);
211
+ </script>
212
+ </body>
213
+ </HtmlDocument>
214
+ };
215
+ } catch (error) {
216
+ if(error.response.data) {
217
+ throw new EntityAccessError(error.message, error.response.data);
218
+ }
219
+ }
220
+ }
221
+
222
+ async fetchSubscriptionInfo(pgSubscription: StorePGSubscription) {
223
+
224
+ const { gateway, gatewaySubscriptionID: id } = pgSubscription;
225
+ const client = this.pc.getClient(gateway);
226
+
227
+ const sub = await client.SubsFetchSubscription(id);
228
+
229
+ if (/ACTIVE/i.test(sub.data.subscription_status)) {
230
+ return {
231
+ active: true
232
+ };
233
+ }
234
+
235
+ }
236
+
237
+ }
@@ -5,8 +5,16 @@ import StorePaymentGateway from "../../../model/entities/StorePaymentGateway.js"
5
5
  import EncryptionService from "../../encryption/EncryptionService.js";
6
6
  import LedgerPaymentGateway from "../LedgerPaymentGateway.js";
7
7
  import SocialMailContext from "../../../model/SocialMailContext.js";
8
- import { CaptureStatus, CheckoutPaymentIntent, Client, Environment, OrdersController, OrderStatus, PayeePaymentMethodPreference, PaypalExperienceLandingPage, PaypalExperienceUserAction } from "@paypal/paypal-server-sdk";
8
+ import { CaptureStatus, CheckoutPaymentIntent, Client, Environment,
9
+ OrdersController, OrderStatus, PayeePaymentMethodPreference,
10
+ SubscriptionsController,
11
+ PaypalExperienceLandingPage, PaypalExperienceUserAction,
12
+ PlanRequestStatus,
13
+ IntervalUnit,
14
+ TenureType} from "@paypal/paypal-server-sdk";
9
15
  import TimedCache from "@entity-access/entity-access/dist/common/cache/TimedCache.js";
16
+ import StorePGSubscription from "../../../model/entities/StorePGSubscription.js";
17
+ import StoreSubscription from "../../../model/entities/StoreSubscription.js";
10
18
 
11
19
  @RegisterSingleton
12
20
  class PaypalClientCache {
@@ -27,7 +35,7 @@ class PaypalClientCache {
27
35
  oAuthClientSecret: secret
28
36
  }
29
37
  });
30
- })
38
+ });
31
39
  }
32
40
 
33
41
  }
@@ -166,4 +174,67 @@ export default class extends LedgerPaymentGateway {
166
174
  // throw new Error("Method not implemented.");
167
175
  }
168
176
 
177
+ async createSubscription(host: string, gateway: StorePaymentGateway, subscription: StoreSubscription) {
178
+
179
+ const { accountID: paymentGatewayID } = gateway;
180
+ const { itemPrice: { priceID: itemPriceID, storeItem: { storeItemID } }} = subscription;
181
+
182
+ let pg = await this.db.storePgItemPrices.statements.select({}, { paymentGatewayID, itemPriceID});
183
+
184
+ const client = this.pc.getClient(gateway);
185
+ const sc = new SubscriptionsController(client);
186
+
187
+ if (!pg) {
188
+
189
+ const plan = await sc.createBillingPlan({
190
+ body: {
191
+ name: itemPriceID.toString(),
192
+ productId: storeItemID.toString(),
193
+ paymentPreferences: {
194
+ autoBillOutstanding: false,
195
+ paymentFailureThreshold:2
196
+ },
197
+ billingCycles: [
198
+ {
199
+ frequency: {
200
+ intervalUnit: IntervalUnit.Year,
201
+ intervalCount: 1
202
+ },
203
+ sequence: 1,
204
+ tenureType: TenureType.Regular,
205
+ }
206
+ ],
207
+ quantitySupported: false,
208
+ status: PlanRequestStatus.Active
209
+ }
210
+ });
211
+
212
+ pg = await this.db.storePgItemPrices.statements.selectOrInsert({
213
+ paymentGatewayID,
214
+ itemPriceID,
215
+ pgPriceID: plan.result.id,
216
+ }, {
217
+ paymentGatewayID,
218
+ itemPriceID,
219
+ });
220
+
221
+ }
222
+
223
+ const planId = pg.pgPriceID;
224
+
225
+ const pgSubscription = await sc.createSubscription({
226
+ body: {
227
+ planId,
228
+ autoRenewal: true,
229
+ }
230
+ });
231
+
232
+ return {
233
+ url: pgSubscription.result.links[0].href
234
+ };
235
+ }
236
+ async fetchSubscriptionInfo(pgSubscription: StorePGSubscription) {
237
+ return {};
238
+ }
239
+
169
240
  }
@@ -10,6 +10,8 @@ import XNode from "@entity-access/server-pages/dist/html/XNode.js";
10
10
  import StorePaymentAttempt from "../../../model/entities/StorePaymentAttempt.js";
11
11
  import StorePaymentGateway from "../../../model/entities/StorePaymentGateway.js";
12
12
  import SecureFetchBuilder from "../../../../common/fetch/SecureFetchBuilder.js";
13
+ import StorePGSubscription from "../../../model/entities/StorePGSubscription.js";
14
+ import StoreSubscription from "../../../model/entities/StoreSubscription.js";
13
15
 
14
16
  const signRequest = (id: any, salt: any, path = "/pg/v1/pay", payload: string = ""): { request: any; verify: any; } => {
15
17
  const request = Buffer.from(payload, "utf-8").toString("base64");
@@ -109,4 +111,11 @@ export default class extends LedgerPaymentGateway {
109
111
  return false;
110
112
  }
111
113
 
114
+ async createSubscription(host: string, gateway: StorePaymentGateway, subscription: StoreSubscription) {
115
+ return {};
116
+ }
117
+ async fetchSubscriptionInfo(pgSubscription: StorePGSubscription) {
118
+ return {};
119
+ }
120
+
112
121
  }
@@ -7,6 +7,8 @@ import SocialMailContext from "../../../model/SocialMailContext.js";
7
7
 
8
8
  import * as stripe from "stripe";
9
9
  import EncryptionService from "../../encryption/EncryptionService.js";
10
+ import StorePGSubscription from "../../../model/entities/StorePGSubscription.js";
11
+ import StoreSubscription from "../../../model/entities/StoreSubscription.js";
10
12
 
11
13
  export default class StripePaymentGateway extends LedgerPaymentGateway {
12
14
 
@@ -74,4 +76,11 @@ export default class StripePaymentGateway extends LedgerPaymentGateway {
74
76
  return s;
75
77
  }
76
78
 
79
+ async createSubscription(host: string, gateway: StorePaymentGateway, subscription: StoreSubscription) {
80
+ return {};
81
+ }
82
+ async fetchSubscriptionInfo(pgSubscription: StorePGSubscription) {
83
+ return {};
84
+ }
85
+
77
86
  }
@@ -43,7 +43,7 @@ export default class extends Page {
43
43
  currency = w.currency;
44
44
  }
45
45
 
46
- const contact = await this.db.storeAccounts.where({userID }, (p) => (x) => x.accountID === p.userID)
46
+ const contact = await this.db.storeAccounts.where({userID }, (p) => (x) => x.userID === p.userID)
47
47
  .include((x) => x.addresses)
48
48
  .first() ?? EntityAccessError.throw(`Please signup as a store user instead of backend user.`);
49
49
 
@@ -0,0 +1,82 @@
1
+ import Inject from "@entity-access/entity-access/dist/di/di.js";
2
+ import { Route } from "@entity-access/server-pages/dist/core/Route.js";
3
+ import Page from "@entity-access/server-pages/dist/Page.js";
4
+ import SocialMailContext from "../../../../../server/model/SocialMailContext.js";
5
+ import StoreConfigService from "../../../../../server/services/store/StoreConfigService.js";
6
+ import WebSiteContentService from "../../../../../server/services/files/WebSiteContentService.js";
7
+ import StorePaymentGatewayService from "../../../../../server/services/store/StorePaymentGatewayService.js";
8
+ import { Symbols } from "../../../../../common/Symbols.js";
9
+ import { Prepare } from "@entity-access/server-pages/dist/decorators/Prepare.js";
10
+ import EntityAccessError from "@entity-access/entity-access/dist/common/EntityAccessError.js";
11
+
12
+
13
+ @Prepare.authorize
14
+ export default class extends Page {
15
+
16
+ @Route
17
+ productID;
18
+
19
+ @Inject
20
+ db: SocialMailContext;
21
+
22
+ @Inject
23
+ storeConfigService: StoreConfigService;
24
+
25
+ @Inject
26
+ siteContentService: WebSiteContentService;
27
+
28
+ @Inject
29
+ pgs: StorePaymentGatewayService;
30
+
31
+ async run() {
32
+
33
+ // only if logged in...
34
+ // 1. Create order
35
+ // 2. Route to Checkout
36
+
37
+ let [ currency ] = this.childPath as any[];
38
+
39
+ const w = await this.siteContentService.getWebSite(this.request.hostName);
40
+ const storeID = w.folderID;
41
+
42
+ if (!currency) {
43
+ currency = w.currency;
44
+ }
45
+
46
+ // todo: optimization
47
+ // do not create new orders if new order for same item exists
48
+ // with only a single item...
49
+
50
+ const { productID } = this;
51
+
52
+ const { price } = await this.storeConfigService.getPrice(productID, currency, this.request.hostName, storeID);
53
+
54
+ const { userID } = this.sessionUser;
55
+
56
+ const contact = await this.db.storeAccounts.where({userID }, (p) => (x) => x.userID === p.userID)
57
+ .include((x) => x.addresses)
58
+ .first() ?? EntityAccessError.throw(`Please signup as a store user instead of backend user.`);
59
+
60
+ const subscription = this.db.storeSubscriptions.add({
61
+ storeID,
62
+ buyerID: contact.accountID,
63
+ status: "pending",
64
+ itemPrice: price
65
+ });
66
+
67
+ subscription[Symbols.enableSaveWithoutLogin] = true;
68
+
69
+ await this.db.saveChanges();
70
+
71
+ const host = this.request.hostName;
72
+
73
+ const c = await this.pgs.createSubscription({ storeID, subscription, host, userID: this.sessionUser.userID });
74
+
75
+ if (c.url) {
76
+ return this.redirect(c.url);
77
+ }
78
+
79
+ return this.content({ body: c.content, contentType: "text/html" });
80
+ }
81
+
82
+ }
@@ -0,0 +1,30 @@
1
+ import Inject from "@entity-access/entity-access/dist/di/di.js";
2
+ import { Route } from "@entity-access/server-pages/dist/core/Route.js";
3
+ import Page from "@entity-access/server-pages/dist/Page.js";
4
+ import StorePaymentGatewayService from "../../../../../server/services/store/StorePaymentGatewayService.js";
5
+ import EncryptionService from "../../../../../server/services/encryption/EncryptionService.js";
6
+
7
+
8
+ export default class extends Page {
9
+
10
+ @Route
11
+ subscriptionID;
12
+
13
+ @Inject
14
+ pgs: StorePaymentGatewayService;
15
+
16
+ @Inject
17
+ ecs: EncryptionService;
18
+
19
+ async run() {
20
+
21
+ let { subscriptionID } = this;
22
+ subscriptionID = this.ecs.general.decrypt(subscriptionID);
23
+ const c = await this.pgs.fetchSubscription(subscriptionID);
24
+ if (c.active) {
25
+ this.cacheControl = "public, max-age=8600";
26
+ }
27
+ return this.redirect("/store/subscriptions/" + c.subscription.subscriptionID);
28
+
29
+ }
30
+ }