payment-kit 1.13.99 → 1.13.100
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/api/src/queues/invoice.ts +4 -7
- package/api/src/queues/payment.ts +3 -36
- package/api/src/queues/subscription.ts +1 -0
- package/api/src/routes/connect/shared.ts +29 -6
- package/api/src/routes/subscriptions.ts +63 -46
- package/api/src/store/models/customer.ts +18 -4
- package/api/src/store/models/invoice.ts +5 -4
- package/blocklet.yml +1 -1
- package/package.json +3 -3
- package/src/pages/customer/subscription/update.tsx +4 -4
|
@@ -42,15 +42,12 @@ export const handleInvoice = async (job: InvoiceJob) => {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
// no payment required
|
|
45
|
-
if (invoice.
|
|
45
|
+
if (invoice.amount_remaining === '0') {
|
|
46
46
|
logger.warn(`invoice does not require payment: ${job.invoiceId}`);
|
|
47
47
|
|
|
48
48
|
await invoice.update({
|
|
49
49
|
paid: true,
|
|
50
50
|
status: 'paid',
|
|
51
|
-
amount_due: '0',
|
|
52
|
-
amount_paid: '0',
|
|
53
|
-
amount_remaining: '0',
|
|
54
51
|
attempt_count: invoice.attempt_count + 1,
|
|
55
52
|
attempted: true,
|
|
56
53
|
status_transitions: { ...invoice.status_transitions, paid_at: dayjs().unix() },
|
|
@@ -101,9 +98,9 @@ export const handleInvoice = async (job: InvoiceJob) => {
|
|
|
101
98
|
// TODO: support partial payment from user balance
|
|
102
99
|
paymentIntent = await PaymentIntent.create({
|
|
103
100
|
livemode: !!invoice.livemode,
|
|
104
|
-
amount: invoice.
|
|
101
|
+
amount: invoice.amount_remaining,
|
|
105
102
|
amount_received: '0',
|
|
106
|
-
amount_capturable:
|
|
103
|
+
amount_capturable: '0',
|
|
107
104
|
customer_id: invoice.customer_id,
|
|
108
105
|
description: descriptionMap[invoice.billing_reason] || '',
|
|
109
106
|
currency_id: invoice.currency_id,
|
|
@@ -160,7 +157,7 @@ export const startInvoiceQueue = async () => {
|
|
|
160
157
|
where: {
|
|
161
158
|
status: 'open',
|
|
162
159
|
collection_method: 'charge_automatically',
|
|
163
|
-
|
|
160
|
+
amount_remaining: { [Op.gt]: '0' },
|
|
164
161
|
},
|
|
165
162
|
});
|
|
166
163
|
|
|
@@ -5,7 +5,7 @@ import dayjs from '../libs/dayjs';
|
|
|
5
5
|
import CustomError from '../libs/error';
|
|
6
6
|
import { events } from '../libs/event';
|
|
7
7
|
import logger from '../libs/logger';
|
|
8
|
-
import { getGasPayerExtra,
|
|
8
|
+
import { getGasPayerExtra, isDelegationSufficientForPayment } from '../libs/payment';
|
|
9
9
|
import createQueue from '../libs/queue';
|
|
10
10
|
import { getDaysUntilDue, getDueUnit } from '../libs/subscription';
|
|
11
11
|
import { MAX_RETRY_COUNT, MIN_RETRY_MAIL, getNextRetry } from '../libs/util';
|
|
@@ -24,7 +24,7 @@ type PaymentJob = {
|
|
|
24
24
|
retryOnError?: boolean;
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
-
export const handlePaymentSucceed = async (paymentIntent: PaymentIntent
|
|
27
|
+
export const handlePaymentSucceed = async (paymentIntent: PaymentIntent) => {
|
|
28
28
|
let invoice;
|
|
29
29
|
if (paymentIntent.invoice_id) {
|
|
30
30
|
invoice = await Invoice.findByPk(paymentIntent.invoice_id);
|
|
@@ -46,15 +46,13 @@ export const handlePaymentSucceed = async (paymentIntent: PaymentIntent, invoice
|
|
|
46
46
|
await invoice.update({
|
|
47
47
|
paid: true,
|
|
48
48
|
status: 'paid',
|
|
49
|
-
amount_due: '0',
|
|
50
49
|
amount_paid: paymentIntent.amount,
|
|
51
50
|
amount_remaining: '0',
|
|
52
51
|
attempt_count: invoice.attempt_count + 1,
|
|
53
52
|
attempted: true,
|
|
54
53
|
status_transitions: { ...invoice.status_transitions, paid_at: dayjs().unix() },
|
|
55
|
-
...invoiceUpdates,
|
|
56
54
|
});
|
|
57
|
-
logger.info(`Invoice ${invoice.id} updated on payment done: ${paymentIntent.id}
|
|
55
|
+
logger.info(`Invoice ${invoice.id} updated on payment done: ${paymentIntent.id}`);
|
|
58
56
|
}
|
|
59
57
|
|
|
60
58
|
if (invoice.subscription_id) {
|
|
@@ -297,37 +295,6 @@ export const handlePayment = async (job: PaymentJob) => {
|
|
|
297
295
|
const client = paymentMethod.getOcapClient();
|
|
298
296
|
const payer = paymentSettings?.payment_method_options.arcblock?.payer;
|
|
299
297
|
|
|
300
|
-
// if we can complete purchase with customer balance
|
|
301
|
-
const balance = isBalanceSufficientForPayment({
|
|
302
|
-
paymentMethod,
|
|
303
|
-
paymentCurrency,
|
|
304
|
-
customer,
|
|
305
|
-
amount: paymentIntent.amount,
|
|
306
|
-
});
|
|
307
|
-
if (balance.sufficient) {
|
|
308
|
-
const tmp = await customer.decreaseTokenBalance(paymentCurrency.id, paymentIntent.amount);
|
|
309
|
-
logger.info(`PaymentIntent capture done: ${paymentIntent.id} with customer balance`, tmp);
|
|
310
|
-
await paymentIntent.update({
|
|
311
|
-
status: 'succeeded',
|
|
312
|
-
amount: '0', // update payment intent amount to 0
|
|
313
|
-
amount_received: '0',
|
|
314
|
-
payment_details: {
|
|
315
|
-
arcblock: {
|
|
316
|
-
tx_hash: '',
|
|
317
|
-
payer: payer as string,
|
|
318
|
-
},
|
|
319
|
-
},
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
await handlePaymentSucceed(paymentIntent, {
|
|
323
|
-
starting_token_balance: tmp.starting,
|
|
324
|
-
ending_token_balance: tmp.ending,
|
|
325
|
-
});
|
|
326
|
-
return;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// FIXME: support partial payment from balance
|
|
330
|
-
|
|
331
298
|
// check balance before capture with transaction
|
|
332
299
|
result = await isDelegationSufficientForPayment({
|
|
333
300
|
paymentMethod,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/indent */
|
|
2
|
+
/* eslint-disable prettier/prettier */
|
|
1
3
|
import { BN } from '@ocap/util';
|
|
2
4
|
|
|
3
5
|
import { estimateMaxGasForTx, hasStakedForGas } from '../../integrations/blockchain/stake';
|
|
@@ -252,8 +254,10 @@ export async function ensureInvoiceForCheckout({
|
|
|
252
254
|
};
|
|
253
255
|
}
|
|
254
256
|
|
|
257
|
+
const currency = await PaymentCurrency.findByPk(checkoutSession.currency_id);
|
|
255
258
|
const { invoice, items } = await ensureInvoiceAndItems({
|
|
256
259
|
customer,
|
|
260
|
+
currency: currency as PaymentCurrency,
|
|
257
261
|
subscription,
|
|
258
262
|
lineItems: await Price.expand(checkoutSession.line_items, { product: true }),
|
|
259
263
|
trailing: !!checkoutSession.subscription_data?.trial_period_days,
|
|
@@ -301,19 +305,37 @@ export async function ensureInvoiceForCheckout({
|
|
|
301
305
|
|
|
302
306
|
export async function ensureInvoiceAndItems({
|
|
303
307
|
customer,
|
|
308
|
+
currency,
|
|
304
309
|
subscription,
|
|
305
310
|
props,
|
|
306
311
|
lineItems,
|
|
307
312
|
trailing,
|
|
308
313
|
metered,
|
|
314
|
+
applyCredit = true,
|
|
309
315
|
}: {
|
|
310
316
|
customer: Customer;
|
|
317
|
+
currency: PaymentCurrency;
|
|
311
318
|
subscription?: Subscription;
|
|
312
319
|
props: TInvoice;
|
|
313
320
|
lineItems: TLineItemExpanded[];
|
|
314
321
|
trailing: boolean; // do we have trailing
|
|
315
322
|
metered: boolean; // is the quantity metered
|
|
323
|
+
applyCredit?: boolean; // should we apply customer credit?
|
|
316
324
|
}): Promise<{ invoice: Invoice; items: InvoiceItem[] }> {
|
|
325
|
+
if (props.total === '0') {
|
|
326
|
+
throw new Error('Invoice total should not be 0');
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// apply possible balance to invoice
|
|
330
|
+
let remaining = props.total;
|
|
331
|
+
let result = { starting: {}, ending: {} };
|
|
332
|
+
if (applyCredit) {
|
|
333
|
+
const balance = customer.getBalanceToApply(currency.id, props.total);
|
|
334
|
+
result = await customer.decreaseTokenBalance(currency.id, balance);
|
|
335
|
+
remaining = new BN(props.total).sub(new BN(balance)).toString();
|
|
336
|
+
logger.info(`Invoice will use customer credit: ${props.total}:${remaining}`, result);
|
|
337
|
+
}
|
|
338
|
+
|
|
317
339
|
const invoice = await Invoice.create({
|
|
318
340
|
livemode: props.livemode,
|
|
319
341
|
number: await customer.getInvoiceNumber(),
|
|
@@ -336,19 +358,20 @@ export async function ensureInvoiceAndItems({
|
|
|
336
358
|
subscription_id: subscription?.id,
|
|
337
359
|
checkout_session_id: props.checkout_session_id || '',
|
|
338
360
|
|
|
361
|
+
total: props.total || '0',
|
|
339
362
|
subtotal: props.total || '0',
|
|
340
|
-
subtotal_excluding_tax: props.total || '0',
|
|
341
363
|
tax: '0',
|
|
342
|
-
|
|
343
|
-
|
|
364
|
+
subtotal_excluding_tax: props.total || '0',
|
|
365
|
+
|
|
366
|
+
amount_due: remaining,
|
|
344
367
|
amount_paid: '0',
|
|
345
|
-
amount_remaining:
|
|
368
|
+
amount_remaining: remaining,
|
|
346
369
|
amount_shipping: '0',
|
|
347
370
|
|
|
348
371
|
starting_balance: '0',
|
|
349
372
|
ending_balance: '0',
|
|
350
|
-
starting_token_balance:
|
|
351
|
-
ending_token_balance:
|
|
373
|
+
starting_token_balance: result.starting,
|
|
374
|
+
ending_token_balance: result.ending,
|
|
352
375
|
|
|
353
376
|
attempt_count: 0,
|
|
354
377
|
attempted: false,
|
|
@@ -508,6 +508,11 @@ const createProration = async (subscription: TSubscription, setup: ReturnType<ty
|
|
|
508
508
|
throw new Error('Subscription should have latest invoice when create proration');
|
|
509
509
|
}
|
|
510
510
|
|
|
511
|
+
const customer = await Customer.findByPk(subscription.customer_id);
|
|
512
|
+
if (!customer) {
|
|
513
|
+
throw new Error('Subscription should have customer when create proration');
|
|
514
|
+
}
|
|
515
|
+
|
|
511
516
|
// 1. get last invoice, and invoice items, filter invoice items that are in licensed recurring mode
|
|
512
517
|
const invoiceItems = await InvoiceItem.findAll({ where: { invoice_id: lastInvoice.id, proration: false } });
|
|
513
518
|
const invoiceItemsExpanded = await Price.expand(invoiceItems.map((x) => x.toJSON()));
|
|
@@ -520,11 +525,11 @@ const createProration = async (subscription: TSubscription, setup: ReturnType<ty
|
|
|
520
525
|
const prorationStart = lastInvoice.period_start;
|
|
521
526
|
const prorationEnd = lastInvoice.period_end;
|
|
522
527
|
const prorationRate = Math.ceil(((prorationEnd - now) / (prorationEnd - prorationStart)) * 1000000);
|
|
523
|
-
let
|
|
528
|
+
let proration = new BN(0);
|
|
524
529
|
const prorations = await Promise.all(
|
|
525
530
|
prorationItems.map((x: TLineItemExpanded & { [key: string]: any }) => {
|
|
526
531
|
const unitAmount = getPriceUintAmountByCurrency(x.price, subscription.currency_id);
|
|
527
|
-
const
|
|
532
|
+
const amount = new BN(unitAmount)
|
|
528
533
|
.mul(new BN(x.quantity))
|
|
529
534
|
.mul(new BN(prorationRate))
|
|
530
535
|
.div(new BN(1000000))
|
|
@@ -533,13 +538,13 @@ const createProration = async (subscription: TSubscription, setup: ReturnType<ty
|
|
|
533
538
|
subscription: subscription.id,
|
|
534
539
|
invoice: x.invoice_id,
|
|
535
540
|
invoiceItem: x.id,
|
|
536
|
-
|
|
541
|
+
amount,
|
|
537
542
|
});
|
|
538
|
-
|
|
543
|
+
proration = proration.add(new BN(amount));
|
|
539
544
|
|
|
540
545
|
return {
|
|
541
546
|
price_id: x.price_id,
|
|
542
|
-
amount: `-${
|
|
547
|
+
amount: `-${amount}`,
|
|
543
548
|
quantity: x.quantity,
|
|
544
549
|
// @ts-ignore
|
|
545
550
|
description: `Unused time on ${x.price.product.name} after ${dayjs().format('lll')}`,
|
|
@@ -564,14 +569,20 @@ const createProration = async (subscription: TSubscription, setup: ReturnType<ty
|
|
|
564
569
|
});
|
|
565
570
|
|
|
566
571
|
// 5. adjust invoice total && update customer token balance
|
|
567
|
-
|
|
568
|
-
let
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
+
const total = setup.amount.setup;
|
|
573
|
+
let remaining = setup.amount.setup;
|
|
574
|
+
let newCredit = '0';
|
|
575
|
+
let appliedCredit = '0';
|
|
576
|
+
if (new BN(total).gte(proration)) {
|
|
577
|
+
// Proration amount is less than total, all proration are used,
|
|
578
|
+
remaining = new BN(total).sub(proration).toString();
|
|
579
|
+
// Besides, we need to try to apply customer credit
|
|
580
|
+
appliedCredit = customer.getBalanceToApply(subscription.currency_id, remaining);
|
|
581
|
+
remaining = new BN(remaining).sub(new BN(appliedCredit)).toString();
|
|
572
582
|
} else {
|
|
573
|
-
|
|
574
|
-
|
|
583
|
+
// Proration amount is greater than total, we need to increase customer credit
|
|
584
|
+
newCredit = proration.sub(new BN(total)).toString();
|
|
585
|
+
remaining = '0';
|
|
575
586
|
}
|
|
576
587
|
|
|
577
588
|
logger.info('subscription proration result', {
|
|
@@ -579,16 +590,20 @@ const createProration = async (subscription: TSubscription, setup: ReturnType<ty
|
|
|
579
590
|
prorationStart,
|
|
580
591
|
prorationEnd,
|
|
581
592
|
prorationRate,
|
|
582
|
-
|
|
593
|
+
proration,
|
|
583
594
|
total,
|
|
584
|
-
|
|
595
|
+
remaining,
|
|
596
|
+
newCredit,
|
|
597
|
+
appliedCredit,
|
|
585
598
|
});
|
|
586
599
|
|
|
587
600
|
return {
|
|
588
601
|
lastInvoice,
|
|
589
602
|
total,
|
|
590
|
-
|
|
603
|
+
remaining,
|
|
591
604
|
prorations,
|
|
605
|
+
newCredit,
|
|
606
|
+
appliedCredit,
|
|
592
607
|
};
|
|
593
608
|
};
|
|
594
609
|
|
|
@@ -738,16 +753,21 @@ router.put('/:id', authPortal, async (req, res) => {
|
|
|
738
753
|
const prorationBehavior = updates.proration_behavior || subscription.proration_behavior || 'none';
|
|
739
754
|
if (prorationBehavior === 'create_prorations') {
|
|
740
755
|
// 1. create proration
|
|
741
|
-
const { lastInvoice,
|
|
756
|
+
const { lastInvoice, remaining, newCredit, appliedCredit, prorations } = await createProration(
|
|
757
|
+
subscription,
|
|
758
|
+
setup
|
|
759
|
+
);
|
|
742
760
|
|
|
743
761
|
// 2. create new invoice: amount according to new subscription items
|
|
744
762
|
// 3. create new invoice items: amount according to new subscription items
|
|
745
763
|
const result = await ensureInvoiceAndItems({
|
|
746
764
|
customer,
|
|
765
|
+
currency: paymentCurrency,
|
|
747
766
|
subscription,
|
|
748
767
|
trailing: false,
|
|
749
768
|
metered: false,
|
|
750
769
|
lineItems: newItems,
|
|
770
|
+
applyCredit: false,
|
|
751
771
|
props: {
|
|
752
772
|
status: 'draft',
|
|
753
773
|
livemode: subscription.livemode,
|
|
@@ -755,7 +775,7 @@ router.put('/:id', authPortal, async (req, res) => {
|
|
|
755
775
|
statement_descriptor: lastInvoice.statement_descriptor,
|
|
756
776
|
period_start: setup.period.start,
|
|
757
777
|
period_end: setup.period.end,
|
|
758
|
-
auto_advance: true,
|
|
778
|
+
auto_advance: true,
|
|
759
779
|
billing_reason: 'subscription_update',
|
|
760
780
|
total: setup.amount.setup,
|
|
761
781
|
currency_id: paymentCurrency.id,
|
|
@@ -793,32 +813,33 @@ router.put('/:id', authPortal, async (req, res) => {
|
|
|
793
813
|
});
|
|
794
814
|
|
|
795
815
|
// 5. adjust invoice total or update customer credit balance
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
816
|
+
const invoiceUpdates: Partial<Invoice> = {
|
|
817
|
+
status: 'open',
|
|
818
|
+
amount_due: remaining,
|
|
819
|
+
amount_remaining: remaining,
|
|
820
|
+
};
|
|
821
|
+
if (appliedCredit !== '0') {
|
|
822
|
+
const creditResult = await customer.decreaseTokenBalance(paymentCurrency.id, appliedCredit);
|
|
823
|
+
invoiceUpdates.starting_token_balance = creditResult.starting;
|
|
824
|
+
invoiceUpdates.ending_token_balance = creditResult.ending;
|
|
825
|
+
logger.info('customer credit applied to invoice after proration', {
|
|
826
|
+
subscription: req.params.id,
|
|
827
|
+
appliedCredit,
|
|
828
|
+
creditResult,
|
|
804
829
|
});
|
|
805
|
-
logger.info('subscription proration used on invoice', { subscription: req.params.id, total });
|
|
806
830
|
}
|
|
807
|
-
if (
|
|
808
|
-
const
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
amount_remaining: '0',
|
|
816
|
-
starting_token_balance: balance.starting,
|
|
817
|
-
ending_token_balance: balance.ending,
|
|
831
|
+
if (newCredit !== '0') {
|
|
832
|
+
const creditResult = await customer.increaseTokenBalance(paymentCurrency.id, newCredit);
|
|
833
|
+
invoiceUpdates.starting_token_balance = creditResult.starting;
|
|
834
|
+
invoiceUpdates.ending_token_balance = creditResult.ending;
|
|
835
|
+
logger.info('subscription proration credit applied to customer', {
|
|
836
|
+
subscription: req.params.id,
|
|
837
|
+
newCredit,
|
|
838
|
+
creditResult,
|
|
818
839
|
});
|
|
819
|
-
logger.info('subscription proration credit to customer', { subscription: req.params.id, credit });
|
|
820
840
|
}
|
|
821
841
|
|
|
842
|
+
await invoice.update(invoiceUpdates);
|
|
822
843
|
await subscription.update(updates);
|
|
823
844
|
|
|
824
845
|
// 6. process the invoice as usual: push into queue
|
|
@@ -989,10 +1010,7 @@ router.post('/:id/update', authPortal, async (req, res) => {
|
|
|
989
1010
|
}
|
|
990
1011
|
|
|
991
1012
|
// validate the request
|
|
992
|
-
const { newItems
|
|
993
|
-
subscription,
|
|
994
|
-
req.body.items
|
|
995
|
-
);
|
|
1013
|
+
const { newItems } = await validateSubscriptionUpdateRequest(subscription, req.body.items);
|
|
996
1014
|
|
|
997
1015
|
// do the simulation
|
|
998
1016
|
const setup = getSubscriptionCreateSetup(newItems, subscription.currency_id, 0);
|
|
@@ -1001,12 +1019,11 @@ router.post('/:id/update', authPortal, async (req, res) => {
|
|
|
1001
1019
|
return res.json({
|
|
1002
1020
|
setup,
|
|
1003
1021
|
total: result.total,
|
|
1004
|
-
|
|
1022
|
+
newCredit: result.newCredit,
|
|
1023
|
+
appliedCredit: result.appliedCredit,
|
|
1024
|
+
remaining: result.remaining,
|
|
1005
1025
|
prorations: result.prorations,
|
|
1006
1026
|
items: newItems,
|
|
1007
|
-
addedItems,
|
|
1008
|
-
deletedItems,
|
|
1009
|
-
updatedItems,
|
|
1010
1027
|
});
|
|
1011
1028
|
} catch (err) {
|
|
1012
1029
|
console.error(err);
|
|
@@ -154,7 +154,17 @@ export class Customer extends Model<InferAttributes<Customer>, InferCreationAttr
|
|
|
154
154
|
return `${this.invoice_prefix}-${padStart(sequence.toString(), 4, '0')}`;
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
public
|
|
157
|
+
public getBalanceToApply(currencyId: string, amount: string) {
|
|
158
|
+
const tokens = this.token_balance || {};
|
|
159
|
+
const balance = tokens[currencyId] || '0';
|
|
160
|
+
return new BN(balance).lt(new BN(amount)) ? balance : amount;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
public async decreaseTokenBalance(currencyId: string, amount: string, dryRun: boolean = false) {
|
|
164
|
+
if (amount === '0') {
|
|
165
|
+
return { starting: {}, ending: {} };
|
|
166
|
+
}
|
|
167
|
+
|
|
158
168
|
const tokens = this.token_balance || {};
|
|
159
169
|
const balance = tokens[currencyId] || '0';
|
|
160
170
|
if (new BN(balance).lt(new BN(amount))) {
|
|
@@ -164,17 +174,21 @@ export class Customer extends Model<InferAttributes<Customer>, InferCreationAttr
|
|
|
164
174
|
const starting = cloneDeep(tokens);
|
|
165
175
|
// NOTE: new object is required to trick sequelize to update the field
|
|
166
176
|
const ending = { ...starting, [currencyId]: new BN(balance).sub(new BN(amount)).toString() };
|
|
167
|
-
|
|
177
|
+
if (!dryRun) {
|
|
178
|
+
await this.update({ token_balance: ending });
|
|
179
|
+
}
|
|
168
180
|
return { starting, ending };
|
|
169
181
|
}
|
|
170
182
|
|
|
171
|
-
public async increaseTokenBalance(currencyId: string, amount: string) {
|
|
183
|
+
public async increaseTokenBalance(currencyId: string, amount: string, dryRun: boolean = false) {
|
|
172
184
|
const tokens = this.token_balance || {};
|
|
173
185
|
const balance = tokens[currencyId] || '0';
|
|
174
186
|
const starting = cloneDeep(tokens);
|
|
175
187
|
// NOTE: new object is required to trick sequelize to update the field
|
|
176
188
|
const ending = { ...starting, [currencyId]: new BN(balance).add(new BN(amount)).toString() };
|
|
177
|
-
|
|
189
|
+
if (!dryRun) {
|
|
190
|
+
await this.update({ token_balance: ending });
|
|
191
|
+
}
|
|
178
192
|
return { starting, ending };
|
|
179
193
|
}
|
|
180
194
|
|
|
@@ -64,13 +64,14 @@ export class Invoice extends Model<InferAttributes<Invoice>, InferCreationAttrib
|
|
|
64
64
|
declare subtotal_excluding_tax: string;
|
|
65
65
|
declare tax: string;
|
|
66
66
|
declare total: string;
|
|
67
|
-
|
|
67
|
+
|
|
68
|
+
declare amount_due: string; // total - amount_paid
|
|
68
69
|
declare amount_paid: string;
|
|
69
|
-
declare amount_remaining: string;
|
|
70
|
+
declare amount_remaining: string; // amount_due - amount_paid
|
|
70
71
|
declare amount_shipping: string;
|
|
71
72
|
|
|
72
|
-
declare starting_balance: string;
|
|
73
|
-
declare ending_balance: string;
|
|
73
|
+
declare starting_balance: string; // usd credit
|
|
74
|
+
declare ending_balance: string; // usd credit
|
|
74
75
|
declare starting_token_balance?: Record<string, string>; // token balances
|
|
75
76
|
declare ending_token_balance?: Record<string, string>; // token balances
|
|
76
77
|
|
package/blocklet.yml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "payment-kit",
|
|
3
|
-
"version": "1.13.
|
|
3
|
+
"version": "1.13.100",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "COMPONENT_STORE_URL=https://test.store.blocklet.dev blocklet dev",
|
|
6
6
|
"eject": "vite eject",
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
"@abtnode/types": "1.16.21",
|
|
111
111
|
"@arcblock/eslint-config": "^0.2.4",
|
|
112
112
|
"@arcblock/eslint-config-ts": "^0.2.4",
|
|
113
|
-
"@did-pay/types": "1.13.
|
|
113
|
+
"@did-pay/types": "1.13.100",
|
|
114
114
|
"@types/cookie-parser": "^1.4.6",
|
|
115
115
|
"@types/cors": "^2.8.17",
|
|
116
116
|
"@types/dotenv-flow": "^3.3.3",
|
|
@@ -149,5 +149,5 @@
|
|
|
149
149
|
"parser": "typescript"
|
|
150
150
|
}
|
|
151
151
|
},
|
|
152
|
-
"gitHead": "
|
|
152
|
+
"gitHead": "d2334fda5fe27f35f3a7d89a1de0251972f20613"
|
|
153
153
|
}
|
|
@@ -54,7 +54,7 @@ export default function CustomerSubscriptionUpdate() {
|
|
|
54
54
|
loading: false,
|
|
55
55
|
priceId: '',
|
|
56
56
|
total: '',
|
|
57
|
-
|
|
57
|
+
remaining: '',
|
|
58
58
|
setup: null,
|
|
59
59
|
prorations: [],
|
|
60
60
|
items: [],
|
|
@@ -86,7 +86,7 @@ export default function CustomerSubscriptionUpdate() {
|
|
|
86
86
|
|
|
87
87
|
const deleted = data.subscription.items.find((si) => data.table.items.some((ti) => ti.price_id === si.price_id));
|
|
88
88
|
if (deleted!.price_id === priceId) {
|
|
89
|
-
setState({ priceId: '', total: '',
|
|
89
|
+
setState({ priceId: '', total: '', remaining: '', setup: null, prorations: [], items: [] });
|
|
90
90
|
return;
|
|
91
91
|
}
|
|
92
92
|
|
|
@@ -108,7 +108,7 @@ export default function CustomerSubscriptionUpdate() {
|
|
|
108
108
|
setState({ priceId, ...result });
|
|
109
109
|
} catch (err) {
|
|
110
110
|
Toast.error(formatError(err));
|
|
111
|
-
setState({ priceId: '', total: '',
|
|
111
|
+
setState({ priceId: '', total: '', remaining: '', setup: null, prorations: [], items: [] });
|
|
112
112
|
}
|
|
113
113
|
};
|
|
114
114
|
|
|
@@ -249,7 +249,7 @@ export default function CustomerSubscriptionUpdate() {
|
|
|
249
249
|
{t('customer.upgrade.due')}
|
|
250
250
|
</Typography>
|
|
251
251
|
<Typography component="p" style={{ fontWeight: 'bold' }}>
|
|
252
|
-
{fromUnitToToken(state.
|
|
252
|
+
{fromUnitToToken(state.remaining, data.subscription.paymentCurrency.decimal)}{' '}
|
|
253
253
|
{data.subscription.paymentCurrency.symbol}
|
|
254
254
|
</Typography>
|
|
255
255
|
</Stack>
|