wire checkout pay to selected payment method
This commit is contained in:
@@ -41,7 +41,7 @@ export class StorefrontCheckoutController {
|
|||||||
|
|
||||||
@Post('shop/checkout/pay')
|
@Post('shop/checkout/pay')
|
||||||
@Throttle(throttleProfiles.checkoutPay)
|
@Throttle(throttleProfiles.checkoutPay)
|
||||||
async pay(@Req() req: Request, @Res() res: Response, @Body() { captcha }: PayCheckoutDto): Promise<void> {
|
async pay(@Req() req: Request, @Res() res: Response, @Body() { captcha, paymentMethod }: PayCheckoutDto): Promise<void> {
|
||||||
const sessionId = this.checkoutSessionCookieService.getSessionId(req, res);
|
const sessionId = this.checkoutSessionCookieService.getSessionId(req, res);
|
||||||
|
|
||||||
if (sessionId) {
|
if (sessionId) {
|
||||||
@@ -64,7 +64,7 @@ export class StorefrontCheckoutController {
|
|||||||
|
|
||||||
const summary = await this.cartService.getCartSummary(cart, discountCodes);
|
const summary = await this.cartService.getCartSummary(cart, discountCodes);
|
||||||
|
|
||||||
const session = await this.checkoutSessionService.createFromCartSummary(summary);
|
const session = await this.checkoutSessionService.createFromCartSummary(summary, paymentMethod);
|
||||||
|
|
||||||
this.checkoutSessionCookieService.setSessionId(req, res, session.id);
|
this.checkoutSessionCookieService.setSessionId(req, res, session.id);
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import { IsNotEmpty, IsString, Length } from 'class-validator';
|
import { IsEnum, IsIn, IsNotEmpty, IsString, Length } from 'class-validator';
|
||||||
import { getAppConfig } from '../../../config';
|
import { getAppConfig, getShopSettingsConfig } from '../../../config';
|
||||||
|
import { PaymentMethod } from '../../payment/types/PaymentMethod';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
captcha: { length: captchaLength }
|
captcha: { length: captchaLength }
|
||||||
} = getAppConfig();
|
} = getAppConfig();
|
||||||
|
|
||||||
|
const { enabledPaymentMethods } = getShopSettingsConfig();
|
||||||
|
|
||||||
export class PayCheckoutDto {
|
export class PayCheckoutDto {
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@IsString()
|
@IsString()
|
||||||
@@ -12,4 +15,9 @@ export class PayCheckoutDto {
|
|||||||
message: `Captcha should be ${captchaLength} characters long`
|
message: `Captcha should be ${captchaLength} characters long`
|
||||||
})
|
})
|
||||||
captcha: string;
|
captcha: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsEnum(PaymentMethod)
|
||||||
|
@IsIn(enabledPaymentMethods, { message: 'Select a valid payment method' })
|
||||||
|
paymentMethod: PaymentMethod;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,8 +38,6 @@ const buildSummary = (overrides: Partial<CookieCartSummary> = {}): CookieCartSum
|
|||||||
discounts: [{ code: 'SAVE1', amount: 1, issueMessage: null }],
|
discounts: [{ code: 'SAVE1', amount: 1, issueMessage: null }],
|
||||||
cartDiscountTotal: 1,
|
cartDiscountTotal: 1,
|
||||||
cartTotalPrice: 9,
|
cartTotalPrice: 9,
|
||||||
cartTotalXmr: '0.06000000',
|
|
||||||
fiatPerXmr: 150,
|
|
||||||
hasManualLines: false,
|
hasManualLines: false,
|
||||||
hasAutoLines: true,
|
hasAutoLines: true,
|
||||||
cartTotalIssueMessage: null,
|
cartTotalIssueMessage: null,
|
||||||
@@ -115,15 +113,15 @@ describe('CheckoutSessionService', () => {
|
|||||||
|
|
||||||
describe('createFromCartSummary', () => {
|
describe('createFromCartSummary', () => {
|
||||||
it('rejects an empty cart', async () => {
|
it('rejects an empty cart', async () => {
|
||||||
await expect(service.createFromCartSummary(buildSummary({ cartExtended: [] }))).rejects.toThrow(
|
await expect(
|
||||||
new BadRequestException('Your cart is empty')
|
service.createFromCartSummary(buildSummary({ cartExtended: [] }), PaymentMethod.Xmr)
|
||||||
);
|
).rejects.toThrow(new BadRequestException('Your cart is empty'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rejects carts that still have unresolved issues', async () => {
|
it('rejects carts that still have unresolved issues', async () => {
|
||||||
await expect(service.createFromCartSummary(buildSummary({ hasIssues: true }))).rejects.toThrow(
|
await expect(
|
||||||
new BadRequestException('Resolve cart issues before paying')
|
service.createFromCartSummary(buildSummary({ hasIssues: true }), PaymentMethod.Xmr)
|
||||||
);
|
).rejects.toThrow(new BadRequestException('Resolve cart issues before paying'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('creates a session, invoice, lines, and valid discounts from the cart summary', async () => {
|
it('creates a session, invoice, lines, and valid discounts from the cart summary', async () => {
|
||||||
@@ -135,7 +133,7 @@ describe('CheckoutSessionService', () => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
const session = await service.createFromCartSummary(summary);
|
const session = await service.createFromCartSummary(summary, PaymentMethod.Xmr);
|
||||||
|
|
||||||
expect(invoiceService.issueInvoice).toHaveBeenCalledWith({
|
expect(invoiceService.issueInvoice).toHaveBeenCalledWith({
|
||||||
paymentMethod: PaymentMethod.Xmr,
|
paymentMethod: PaymentMethod.Xmr,
|
||||||
@@ -180,7 +178,8 @@ describe('CheckoutSessionService', () => {
|
|||||||
await service.createFromCartSummary(
|
await service.createFromCartSummary(
|
||||||
buildSummary({
|
buildSummary({
|
||||||
discounts: [{ code: 'BAD', amount: null, issueMessage: 'Invalid discount code' }]
|
discounts: [{ code: 'BAD', amount: null, issueMessage: 'Invalid discount code' }]
|
||||||
})
|
}),
|
||||||
|
PaymentMethod.Xmr
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(discountRepo.create).not.toHaveBeenCalled();
|
expect(discountRepo.create).not.toHaveBeenCalled();
|
||||||
@@ -190,6 +189,17 @@ describe('CheckoutSessionService', () => {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('issues a bitcoin checkout invoice when that payment method is selected', async () => {
|
||||||
|
await service.createFromCartSummary(buildSummary(), PaymentMethod.Btc);
|
||||||
|
|
||||||
|
expect(invoiceService.issueInvoice).toHaveBeenCalledWith({
|
||||||
|
paymentMethod: PaymentMethod.Btc,
|
||||||
|
reason: InvoiceReason.Checkout,
|
||||||
|
contextId: 'session-uuid',
|
||||||
|
amountFiat: 9
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('findById', () => {
|
describe('findById', () => {
|
||||||
@@ -200,7 +210,14 @@ describe('CheckoutSessionService', () => {
|
|||||||
await expect(service.findById('session-1')).resolves.toBe(session);
|
await expect(service.findById('session-1')).resolves.toBe(session);
|
||||||
expect(sessionRepo.findOne).toHaveBeenCalledWith({
|
expect(sessionRepo.findOne).toHaveBeenCalledWith({
|
||||||
where: { id: 'session-1' },
|
where: { id: 'session-1' },
|
||||||
relations: ['lines', 'discounts', 'invoice', 'invoice.moneroDetails', 'invoice.payments']
|
relations: [
|
||||||
|
'lines',
|
||||||
|
'discounts',
|
||||||
|
'invoice',
|
||||||
|
'invoice.moneroDetails',
|
||||||
|
'invoice.btcDetails',
|
||||||
|
'invoice.payments'
|
||||||
|
]
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -30,7 +30,10 @@ export class CheckoutSessionService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async createFromCartSummary(summary: CookieCartSummary): Promise<CheckoutSession> {
|
async createFromCartSummary(
|
||||||
|
summary: CookieCartSummary,
|
||||||
|
requestedPaymentMethod: PaymentMethod
|
||||||
|
): Promise<CheckoutSession> {
|
||||||
if (summary.cartExtended.length === 0) {
|
if (summary.cartExtended.length === 0) {
|
||||||
throw new BadRequestException('Your cart is empty');
|
throw new BadRequestException('Your cart is empty');
|
||||||
}
|
}
|
||||||
@@ -42,7 +45,7 @@ export class CheckoutSessionService {
|
|||||||
const sessionId = randomUUID();
|
const sessionId = randomUUID();
|
||||||
|
|
||||||
const invoice = await this.invoiceService.issueInvoice({
|
const invoice = await this.invoiceService.issueInvoice({
|
||||||
paymentMethod: PaymentMethod.Xmr,
|
paymentMethod: requestedPaymentMethod,
|
||||||
reason: InvoiceReason.Checkout,
|
reason: InvoiceReason.Checkout,
|
||||||
contextId: sessionId,
|
contextId: sessionId,
|
||||||
amountFiat: summary.cartTotalPrice
|
amountFiat: summary.cartTotalPrice
|
||||||
|
|||||||
Reference in New Issue
Block a user