@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.
- package/dist/server/model/SocialMailContext.d.ts +8 -0
- package/dist/server/model/SocialMailContext.d.ts.map +1 -1
- package/dist/server/model/SocialMailContext.js +8 -0
- package/dist/server/model/SocialMailContext.js.map +1 -1
- package/dist/server/model/SocialMailContextEvents.d.ts.map +1 -1
- package/dist/server/model/SocialMailContextEvents.js +9 -0
- package/dist/server/model/SocialMailContextEvents.js.map +1 -1
- package/dist/server/model/entities/HostedDomain.d.ts +2 -0
- package/dist/server/model/entities/HostedDomain.d.ts.map +1 -1
- package/dist/server/model/entities/HostedDomain.js.map +1 -1
- package/dist/server/model/entities/StoreAccount.d.ts +2 -0
- package/dist/server/model/entities/StoreAccount.d.ts.map +1 -1
- package/dist/server/model/entities/StoreAccount.js.map +1 -1
- package/dist/server/model/entities/StoreItem.d.ts +1 -1
- package/dist/server/model/entities/StoreItem.d.ts.map +1 -1
- package/dist/server/model/entities/StoreItem.js +1 -1
- package/dist/server/model/entities/StoreItem.js.map +1 -1
- package/dist/server/model/entities/StoreItemPrice.d.ts +10 -0
- package/dist/server/model/entities/StoreItemPrice.d.ts.map +1 -1
- package/dist/server/model/entities/StoreItemPrice.js +14 -0
- package/dist/server/model/entities/StoreItemPrice.js.map +1 -1
- package/dist/server/model/entities/StorePGItemPrice.d.ts +8 -0
- package/dist/server/model/entities/StorePGItemPrice.d.ts.map +1 -0
- package/dist/server/model/entities/StorePGItemPrice.js +36 -0
- package/dist/server/model/entities/StorePGItemPrice.js.map +1 -0
- package/dist/server/model/entities/StorePGSubscription.d.ts +13 -0
- package/dist/server/model/entities/StorePGSubscription.d.ts.map +1 -0
- package/dist/server/model/entities/StorePGSubscription.js +62 -0
- package/dist/server/model/entities/StorePGSubscription.js.map +1 -0
- package/dist/server/model/entities/StorePaymentGateway.d.ts +5 -1
- package/dist/server/model/entities/StorePaymentGateway.d.ts.map +1 -1
- package/dist/server/model/entities/StorePaymentGateway.js +1 -1
- package/dist/server/model/entities/StorePaymentGateway.js.map +1 -1
- package/dist/server/model/entities/StoreSubscription.d.ts +36 -0
- package/dist/server/model/entities/StoreSubscription.d.ts.map +1 -0
- package/dist/server/model/entities/StoreSubscription.js +81 -0
- package/dist/server/model/entities/StoreSubscription.js.map +1 -0
- package/dist/server/model/entities/WebSite.d.ts +4 -0
- package/dist/server/model/entities/WebSite.d.ts.map +1 -1
- package/dist/server/model/entities/WebSite.js.map +1 -1
- package/dist/server/model/entities/WebSiteSubscription.d.ts +12 -0
- package/dist/server/model/entities/WebSiteSubscription.d.ts.map +1 -0
- package/dist/server/model/entities/WebSiteSubscription.js +54 -0
- package/dist/server/model/entities/WebSiteSubscription.js.map +1 -0
- package/dist/server/model/events/StorePGItemPriceEvents.d.ts +5 -0
- package/dist/server/model/events/StorePGItemPriceEvents.d.ts.map +1 -0
- package/dist/server/model/events/StorePGItemPriceEvents.js +4 -0
- package/dist/server/model/events/StorePGItemPriceEvents.js.map +1 -0
- package/dist/server/model/events/StorePGSubscriptionEvents.d.ts +7 -0
- package/dist/server/model/events/StorePGSubscriptionEvents.d.ts.map +1 -0
- package/dist/server/model/events/StorePGSubscriptionEvents.js +12 -0
- package/dist/server/model/events/StorePGSubscriptionEvents.js.map +1 -0
- package/dist/server/model/events/StoreSubscriptionEvents.d.ts +7 -0
- package/dist/server/model/events/StoreSubscriptionEvents.d.ts.map +1 -0
- package/dist/server/model/events/StoreSubscriptionEvents.js +12 -0
- package/dist/server/model/events/StoreSubscriptionEvents.js.map +1 -0
- package/dist/server/model/events/VerificationTokenEvents.js +2 -2
- package/dist/server/model/events/VerificationTokenEvents.js.map +1 -1
- package/dist/server/seed/ui/seed-ui.js +1 -1
- package/dist/server/services/store/LedgerPaymentGateway.d.ts +10 -0
- package/dist/server/services/store/LedgerPaymentGateway.d.ts.map +1 -1
- package/dist/server/services/store/LedgerPaymentGateway.js.map +1 -1
- package/dist/server/services/store/StorePaymentGatewayService.d.ts +16 -0
- package/dist/server/services/store/StorePaymentGatewayService.d.ts.map +1 -1
- package/dist/server/services/store/StorePaymentGatewayService.js +30 -0
- package/dist/server/services/store/StorePaymentGatewayService.js.map +1 -1
- package/dist/server/services/store/gateways/cashfree.d.ts +32 -0
- package/dist/server/services/store/gateways/cashfree.d.ts.map +1 -0
- package/dist/server/services/store/gateways/cashfree.js +192 -0
- package/dist/server/services/store/gateways/cashfree.js.map +1 -0
- package/dist/server/services/store/gateways/paypal.d.ts +6 -0
- package/dist/server/services/store/gateways/paypal.d.ts.map +1 -1
- package/dist/server/services/store/gateways/paypal.js +53 -1
- package/dist/server/services/store/gateways/paypal.js.map +1 -1
- package/dist/server/services/store/gateways/phone-pe.d.ts +4 -0
- package/dist/server/services/store/gateways/phone-pe.d.ts.map +1 -1
- package/dist/server/services/store/gateways/phone-pe.js +6 -0
- package/dist/server/services/store/gateways/phone-pe.js.map +1 -1
- package/dist/server/services/store/gateways/stripe.d.ts +4 -0
- package/dist/server/services/store/gateways/stripe.d.ts.map +1 -1
- package/dist/server/services/store/gateways/stripe.js +6 -0
- package/dist/server/services/store/gateways/stripe.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/wwwroot/routes/store/buy/[productID]/get.js +1 -1
- package/dist/wwwroot/routes/store/buy/[productID]/get.js.map +1 -1
- package/dist/wwwroot/routes/store/subscribe/[productID]/get.d.ts +14 -0
- package/dist/wwwroot/routes/store/subscribe/[productID]/get.d.ts.map +1 -0
- package/dist/wwwroot/routes/store/subscribe/[productID]/get.js +80 -0
- package/dist/wwwroot/routes/store/subscribe/[productID]/get.js.map +1 -0
- package/dist/wwwroot/routes/store/subscribe-callback/[subscriptionID]/get.d.ts +10 -0
- package/dist/wwwroot/routes/store/subscribe-callback/[subscriptionID]/get.d.ts.map +1 -0
- package/dist/wwwroot/routes/store/subscribe-callback/[subscriptionID]/get.js +38 -0
- package/dist/wwwroot/routes/store/subscribe-callback/[subscriptionID]/get.js.map +1 -0
- package/package.json +3 -2
- package/src/server/model/SocialMailContext.ts +12 -0
- package/src/server/model/SocialMailContextEvents.ts +10 -0
- package/src/server/model/entities/HostedDomain.ts +2 -0
- package/src/server/model/entities/StoreAccount.ts +2 -0
- package/src/server/model/entities/StoreItem.ts +1 -1
- package/src/server/model/entities/StoreItemPrice.ts +19 -0
- package/src/server/model/entities/StorePGItemPrice.ts +24 -0
- package/src/server/model/entities/StorePGSubscription.ts +48 -0
- package/src/server/model/entities/StorePaymentGateway.ts +5 -1
- package/src/server/model/entities/StoreSubscription.ts +75 -0
- package/src/server/model/entities/WebSite.ts +4 -0
- package/src/server/model/entities/WebSiteSubscription.ts +44 -0
- package/src/server/model/events/StorePGItemPriceEvents.ts +5 -0
- package/src/server/model/events/StorePGSubscriptionEvents.ts +17 -0
- package/src/server/model/events/StoreSubscriptionEvents.ts +16 -0
- package/src/server/model/events/VerificationTokenEvents.ts +2 -2
- package/src/server/seed/ui/seed-ui.ts +1 -1
- package/src/server/services/store/LedgerPaymentGateway.ts +6 -0
- package/src/server/services/store/StorePaymentGatewayService.ts +42 -0
- package/src/server/services/store/gateways/cashfree.tsx +237 -0
- package/src/server/services/store/gateways/paypal.tsx +73 -2
- package/src/server/services/store/gateways/phone-pe.tsx +9 -0
- package/src/server/services/store/gateways/stripe.tsx +9 -0
- package/src/wwwroot/routes/store/buy/[productID]/get.tsx +1 -1
- package/src/wwwroot/routes/store/subscribe/[productID]/get.tsx +82 -0
- 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.
|
|
157
|
-
&&
|
|
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.
|
|
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,
|
|
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.
|
|
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
|
+
}
|