Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

전체 환불 및 부분 환불에 대한 테스트 시나리오 강화. #159

Merged
merged 1 commit into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fake-toss-payments-server",
"version": "2.0.3",
"version": "2.0.4",
"description": "Fake toss-payments server for testing",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down
4 changes: 2 additions & 2 deletions packages/api/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "toss-payments-server-api",
"version": "2.0.3",
"version": "2.0.4",
"description": "Toss Payments Server API",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand All @@ -15,7 +15,7 @@
},
"homepage": "https://github.com/samchon/fake-toss-payments-server",
"dependencies": {
"@nestia/fetcher": "^2.0.3",
"@nestia/fetcher": "^2.0.4",
"typia": "^5.0.4"
},
"keywords": [
Expand Down
2 changes: 1 addition & 1 deletion packages/api/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"info": {
"title": "Toss Payments API",
"description": "Built by [fake-toss-payments-server](https://github.com/samchon/fake-toss-payments-server) with [nestia](https://github.com/samchon/nestia)",
"version": "2.0.3",
"version": "2.0.4",
"license": {
"name": "MIT"
}
Expand Down
4 changes: 2 additions & 2 deletions src/api/functional/internal/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { NestiaSimulator } from "../../utils/NestiaSimulator";
* @param input 웹훅 이벤트 정보
* @author Jeongho Nam - https://github.com/samchon
*
* @controller FakeTossInternalController.webhook
* @controller [object Object]
* @path POST /internal/webhook
* @nestia Generated by Nestia - https://github.com/samchon/nestia
*/
Expand Down Expand Up @@ -105,7 +105,7 @@ export namespace webhook {
* @security basic
* @author Jeongho Nam - https://github.com/samchon
*
* @controller FakeTossInternalController.deposit
* @controller [object Object]
* @path GET /internal/:paymentKey/deposit
* @nestia Generated by Nestia - https://github.com/samchon/nestia
*/
Expand Down
2 changes: 1 addition & 1 deletion src/api/functional/v1/billing/authorizations/card/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { NestiaSimulator } from "../../../../../utils/NestiaSimulator";
* @security basic
* @author Jeongho Nam - https://github.com/samchon
*
* @controller FakeTossBillingController.store
* @controller [object Object]
* @path POST /v1/billing/authorizations/card
* @nestia Generated by Nestia - https://github.com/samchon/nestia
*/
Expand Down
2 changes: 1 addition & 1 deletion src/api/functional/v1/billing/authorizations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export * as card from "./card";
* @security basic
* @author Jeongho Nam - https://github.com/samchon
*
* @controller FakeTossBillingController.at
* @controller [object Object]
* @path POST /v1/billing/authorizations/:billingKey
* @nestia Generated by Nestia - https://github.com/samchon/nestia
*/
Expand Down
2 changes: 1 addition & 1 deletion src/api/functional/v1/billing/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export * as authorizations from "./authorizations";
* @security basic
* @author Jeongho Nam - https://github.com/samchon
*
* @controller FakeTossBillingController.pay
* @controller [object Object]
* @path POST /v1/billing/:billingKey
* @nestia Generated by Nestia - https://github.com/samchon/nestia
*/
Expand Down
4 changes: 2 additions & 2 deletions src/api/functional/v1/cash_receipts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { NestiaSimulator } from "../../../utils/NestiaSimulator";
* @security basic
* @author Jeongho Nam - https://github.com/samchon
*
* @controller FakeTossCashReceiptsController.store
* @controller [object Object]
* @path POST /v1/cash-receipts
* @nestia Generated by Nestia - https://github.com/samchon/nestia
*/
Expand Down Expand Up @@ -99,7 +99,7 @@ export namespace store {
* @security basic
* @author Jeongho Nam - https://github.com/samchon
*
* @controller FakeTossCashReceiptsController.cancel
* @controller [object Object]
* @path POST /v1/cash-receipts/:receiptKey/cancel
* @nestia Generated by Nestia - https://github.com/samchon/nestia
*/
Expand Down
8 changes: 4 additions & 4 deletions src/api/functional/v1/payments/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { NestiaSimulator } from "../../../utils/NestiaSimulator";
* @returns 결제 정보
* @author Jeongho Nam - https://github.com/samchon
*
* @controller FakeTossPaymentsController.at
* @controller [object Object]
* @path GET /v1/payments/:paymentKey
* @nestia Generated by Nestia - https://github.com/samchon/nestia
*/
Expand Down Expand Up @@ -117,7 +117,7 @@ export namespace at {
* @security basic
* @author Jeongho Nam - https://github.com/samchon
*
* @controller FakeTossPaymentsController.key_in
* @controller [object Object]
* @path POST /v1/payments/key-in
* @nestia Generated by Nestia - https://github.com/samchon/nestia
*/
Expand Down Expand Up @@ -207,7 +207,7 @@ export namespace key_in {
* @security basic
* @author Jeongho Nam - https://github.com/samchon
*
* @controller FakeTossPaymentsController.approve
* @controller [object Object]
* @path POST /v1/payments/:paymentKey
* @nestia Generated by Nestia - https://github.com/samchon/nestia
*/
Expand Down Expand Up @@ -296,7 +296,7 @@ export namespace approve {
* @security basic
* @author Jeongho Nam - https://github.com/samchon
*
* @controller FakeTossPaymentsController.cancel
* @controller [object Object]
* @path POST /v1/payments/:paymentKey/cancel
* @nestia Generated by Nestia - https://github.com/samchon/nestia
*/
Expand Down
2 changes: 1 addition & 1 deletion src/api/functional/v1/virtual_accounts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { NestiaSimulator } from "../../../utils/NestiaSimulator";
* @security basic
* @author Jeongho Nam - https://github.com/samchon
*
* @controller FakeTossVirtualAccountsController.store
* @controller [object Object]
* @path POST /v1/virtual-accounts
* @nestia Generated by Nestia - https://github.com/samchon/nestia
*/
Expand Down
21 changes: 16 additions & 5 deletions src/controllers/FakeTossPaymentsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,23 +166,34 @@ export class FakeTossPaymentsController {
FakeTossUserAuth.authorize(request);

const payment: ITossPayment = FakeTossStorage.payments.get(paymentKey);
const amount: number = input.cancelAmount ?? payment.totalAmount;

if (payment.balanceAmount < amount)
throw new nest.UnprocessableEntityException(
"Balance amount is not enough.",
);

payment.status = "CANCELED";
payment.cancels ??= [];
payment.cancels.push({
cancelAmount: input.cancelAmount || payment.totalAmount,
cancelAmount: amount,
cancelReason: input.cancelReason,
taxFreeAmount: input.taxFreeAmount || 0,
taxAmount: input.taxAmount || 0,
refundableAmount: input.refundableAmount || payment.totalAmount,
taxFreeAmount: input.taxFreeAmount ?? 0,
taxAmount: input.taxAmount ?? 0,
refundableAmount: input.refundableAmount ?? payment.totalAmount,
canceledAt: new Date().toISOString(),
});
payment.balanceAmount -= amount;

FakeTossWebhookProvider.webhook({
eventType: "PAYMENT_STATUS_CHANGED",
data: {
paymentKey: payment.paymentKey,
orderId: payment.orderId,
status: "CANCELED",
status:
payment.balanceAmount === 0
? "CANCELED"
: "PARTIAL_CANCELED",
},
}).catch(() => {});

Expand Down
77 changes: 77 additions & 0 deletions test/features/internal/validate_fake_payment_cancel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { TestValidator } from "@nestia/e2e";
import { sleep_for } from "tstl";
import typia from "typia";

import toss from "toss-payments-server-api";
import { ITossPayment } from "toss-payments-server-api/lib/structures/ITossPayment";
import { ITossPaymentWebhook } from "toss-payments-server-api/lib/structures/ITossPaymentWebhook";

import { FakeTossStorage } from "../../../src/providers/FakeTossStorage";
import { TestConnection } from "../../internal/TestConnection";

export const validate_fake_payment_cancel =
(generator: () => Promise<ITossPayment>, virtual: boolean) => async () => {
// 결제 완료
const init: ITossPayment = await generator();

// 검증 로직 준비
const validate = (cancelled: boolean) => (p: ITossPayment) => {
TestValidator.equals("balanceAmount")(
cancelled ? 0 : p.totalAmount,
)(p.balanceAmount);
TestValidator.equals("cancelAmount")(
(p.cancels ?? [])
.map((c) => c.cancelAmount)
.reduce((a, b) => a + b, 0),
)(cancelled ? p.totalAmount : 0);
TestValidator.equals("cancels?.length")(cancelled ? 1 : 0)(
p.cancels?.length ?? 0,
);
TestValidator.predicate("cancel_history")(
cancelled
? () =>
p.cancels?.length === 1 &&
p.cancels?.[0].cancelAmount === p.totalAmount
: () => !p.cancels?.length,
);
};

// 결제 취소 전 검증
validate(false)(init);

// 결제 취소하기 (전액)
const cancelled: ITossPayment =
await toss.functional.v1.payments.cancel(
TestConnection.FAKE,
init.paymentKey,
{
paymentKey: init.paymentKey,
cancelReason: "테스트 결제 취소",
cancelAmount: init.totalAmount,
refundReceiveAccount: virtual
? {
bank: "국민은행",
accountNumber: "12345678901234",
holderName: "홍길동",
}
: undefined,
},
);
typia.assert(cancelled);
validate(true)(cancelled);

// 웹훅 검증
await sleep_for(50);
const webhook: ITossPaymentWebhook = FakeTossStorage.webhooks.get(
init.paymentKey,
);
TestValidator.equals("status")(webhook.data.status)("CANCELED");

// 결제 내역 재 조회하여 검증
const reloaded: ITossPayment = await toss.functional.v1.payments.at(
TestConnection.FAKE,
init.paymentKey,
);
typia.assert(reloaded);
validate(true)(reloaded);
};
44 changes: 44 additions & 0 deletions test/features/internal/validate_fake_payment_cancel_over.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { TestValidator } from "@nestia/e2e";
import typia from "typia";

import toss from "toss-payments-server-api";
import { ITossPayment } from "toss-payments-server-api/lib/structures/ITossPayment";

import { TestConnection } from "../../internal/TestConnection";

export const validate_fake_payment_cancel_over =
(generator: () => Promise<ITossPayment>, virtual: boolean) => async () => {
// 결제 완료
const payment: ITossPayment = await generator();

// 결제 취소하기 (전액 + 100)
await TestValidator.error("over")(() =>
toss.functional.v1.payments.cancel(
TestConnection.FAKE,
payment.paymentKey,
{
paymentKey: payment.paymentKey,
cancelReason: "테스트 결제 취소",
cancelAmount: payment.totalAmount + 100,
refundReceiveAccount: virtual
? {
bank: "국민은행",
accountNumber: "12345678901234",
holderName: "홍길동",
}
: undefined,
},
),
);

// 결제 내역 재 조회하여 다시 검증
const reloaded: ITossPayment = await toss.functional.v1.payments.at(
TestConnection.FAKE,
payment.paymentKey,
);
typia.assert(reloaded);
TestValidator.equals("balance")(payment.balanceAmount)(
payment.totalAmount,
);
TestValidator.equals("cancels.length")(payment.cancels?.length ?? 0)(0);
};
69 changes: 69 additions & 0 deletions test/features/internal/validate_fake_payment_cancel_partial.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { ArrayUtil, TestValidator } from "@nestia/e2e";
import { sleep_for } from "tstl";
import typia from "typia";

import toss from "toss-payments-server-api";
import { ITossPayment } from "toss-payments-server-api/lib/structures/ITossPayment";

import { FakeTossStorage } from "../../../src/providers/FakeTossStorage";
import { TestConnection } from "../../internal/TestConnection";

export const validate_fake_payment_cancel_partial =
(generator: () => Promise<ITossPayment>, virtual: boolean) => async () => {
// 결제 완료
const init: ITossPayment = await generator();

// 검증 로직 준비
const validate = (count: number) => (p: ITossPayment) => {
const expected: number = (p.totalAmount / 5) * count;

// VALIDATE CANCELLED AMOUNT
TestValidator.equals("balanceAmount")(p.totalAmount - expected)(
p.balanceAmount,
);
TestValidator.equals("cancelAmount")(
(p.cancels ?? [])
.map((c) => c.cancelAmount)
.reduce((a, b) => a + b, 0),
)(expected);
TestValidator.equals("cancels?.length")(count)(
p.cancels?.length ?? 0,
);

// VALIDATE WEBHOOOK
if (count !== 0) {
const webhook = FakeTossStorage.webhooks.get(p.paymentKey);
TestValidator.equals("status")(webhook.data.status)(
count !== 5 ? "PARTIAL_CANCELED" : "CANCELED",
);
}
};

// 결제 취소 전 검증
await sleep_for(100);
validate(0)(init);

// 5 회에 걸쳐 분할 취소 하기
await ArrayUtil.asyncRepeat(5)(async (i) => {
const cancelled: ITossPayment =
await toss.functional.v1.payments.cancel(
TestConnection.FAKE,
init.paymentKey,
{
paymentKey: init.paymentKey,
cancelReason: "테스트 결제 취소",
cancelAmount: init.totalAmount / 5,
refundReceiveAccount: virtual
? {
bank: "국민은행",
accountNumber: "12345678901234",
holderName: "홍길동",
}
: undefined,
},
);
typia.assert(cancelled);
await sleep_for(100);
validate(i + 1)(cancelled);
});
};
Loading
Loading