이번 포스팅은 바로 앞전의 포스팅에서 구현한 결제 주문 비즈니스로직을 테스트하는 과정을 거친다. 이전 포스팅에 이를 모두 나타내고 싶었지만 글이 너무 길어지는 것을 염두해 테스팅부분을 개별 포스팅에 다루고자 한다.
이번 포스팅 뿐만 아니라 이번 시리즈는 앞전의 포스팅들과 모두 연결되므로 꼭 사전에 보고 오시길 바랍니다. 하나의 프로젝트성 시리즈입니다.
Postman
을 통한 전체 프로세스 알아보기해당 프로세스 전개는 이전 포스팅에서 다뤄보았던 비즈니스 로직의 흐름과 동일하다.
product가 생성되어야 할 것이고, 판매 대리인을 위한 link가 구축된 후 해당 link와 product를 통해서 order와 order_item이 생성되어야할 것이다. 그리고 그 과정에서 Stripe를 통한 결제 시스템이 개입된다.
docker
환경에서 StandAlone-Application
을 통한 더미데이터 생성물론 유저, 상품(product), 주문(order), 주문 상품(order_items)을 생성하고 수정및 삭제하는 것과 관련된 핸들러 함수를 다 만들어주긴 하였지만, 일부에 한해서 조금 더 빠른 테스트를 얻고자 더미 데이터를 생성하였다.
NestJS와 TypeORM 환경에서 더미데이터를 생성하는 방법엔 몇 가지가 있다.
일전에 본인도 다뤄보았던 nestjs-seeder와 typeorm-seeding이 그 중 하나이다. 이 둘중에선 왠만하면 typeorm-seeding을 사용하는 것을 권장한다.
이번의 경우엔 위의 두 방법이 아닌, nestjs에서 제공하는 "StandAlone Application" 방법을 사용하여 더미 데이터를 생성할 수 있었다.
nestjs_docs standalone-application ✔
Standalone Application에 대해 깊게 설명하면 주제를 너무 벗어나므로 간단히 알아보고 바로 더미데이터를 생성한 코드를 확인해보자.
NestJS의 Standalone Application은 NestJS 프레임워크를 사용하여 독립 실행형 애플리케이션을 개발하는 방법을 의미한다. Standalone Application은 NestJS의 모듈 시스템과 런타임 환경을 사용하여 독립적으로 실행되는 애플리케이션을 구축하는 데 사용되는데, 이는 NestJS의 훌륭한 확장성과 모듈성을 제공하면서 독립 실행형 애플리케이션을 개발할 수 있도록 해준다. 즉, 우리가 메인으로 실행하는 AppModule과 독자적으로 실행이되지만 AppModule의 클래스들을 의존성으로 불러올 수 있는 유용한 장점을 지닌다. (물론 동일한 프로세스 아래에서 동작한다)
✔ ambassador-seeder
// ambassador.seeder.ts
import { NestFactory } from "@nestjs/core";
import { AppModule } from "../app.module";
import { UserService } from "../user/user.service";
import { faker } from "@faker-js/faker";
import * as bcrypt from "bcryptjs";
(async () => {
const app = await NestFactory.createApplicationContext(AppModule);
// 의존성으로 UserService를 불러옴
const userService = app.get(UserService);
const password = await bcrypt.hash("1234", 12);
for(let i = 0; i < 30; i++) {
await userService.save({
first_name: faker.person.firstName(),
last_name: faker.person.lastName(),
email: faker.internet.email(),
password,
is_ambassador: true,
});
}
process.exit();
})();
✔ order-seeder
// order.seeder.ts
import { NestFactory } from "@nestjs/core";
import { AppModule } from "../app.module";
import { faker } from "@faker-js/faker";
import { randomInt } from "crypto";
import { OrderService } from "../order/order.service";
import { OrderItemService } from "../order/order-item.service";
(async () => {
const app = await NestFactory.createApplicationContext(AppModule);
const orderService = app.get(OrderService);
const orderItemService = app.get(OrderItemService)
for(let i = 0; i < 30; i++) {
const order = await orderService.save({
user_id: randomInt(4, 33),
code: faker.lorem.slug(2),
ambassador_email: faker.internet.email(),
first_name: faker.person.firstName(),
last_name: faker.person.lastName(),
email: faker.internet.email(),
complete: true,
});
for (let j=0; j < randomInt(1, 5); j++) {
await orderItemService.save({
order,
product_title: faker.lorem.words(2),
price: randomInt(10, 100),
quantity: randomInt(1, 5),
admin_revenue: randomInt(10, 100),
ambassador_revenue: randomInt(1, 10),
})
}
}
process.exit();
})();
✔ product-seeder
// product.seeder.ts
import { NestFactory } from "@nestjs/core";
import { AppModule } from "../app.module";
import { faker } from "@faker-js/faker";
import { ProductService } from "../product/product.service";
import { randomInt } from "crypto";
(async () => {
const app = await NestFactory.createApplicationContext(AppModule);
const productService = app.get(ProductService);
for(let i = 0; i < 30; i++) {
await productService.save({
title: faker.lorem.words(2),
description: faker.lorem.words(10),
image: faker.image.url({
width: 200,
height: 200,
}),
price: randomInt(10, 100),
});
}
process.exit();
})();
이제 위의 standalone-application (즉, 독립적 애플리케이션)을 실행시켜주어야한다. 현재 도커환경에서 전체 애플리케이션을 실행중인데, 이러한 독립적 애플리케이션은 별개적으로 실행시켜야한다. 물론, 도커가 실행할때 동시에 실행시켜주게 한다는 등의 방법이 있을지도 모른다. 일단은 추후 알아보도록 하고, 수동적으로 실행시켜주자.
방법은 아주 간단하다.
현재 실행중인 폴더 디렉토리에서 아래의 명령어를 입력한뒤
docker-compose exec backend(서버 이름) sh
package.json에서 등록한 실행 명령어대로 (ex _ npm run seed:ambassadors) 입력하면 된다!!!!
"scripts": {
"build": "nest build",
// ... ...
"seed:ambassadors": "ts-node src/commands/ambassador.seeder.ts",
"seed:products": "ts-node src/commands/product.seeder.ts",
"seed:orders": "ts-node src/commands/order.seeder.ts"
},
이로써 독립적인 애플리케이션을 실행할 수 있게 된다.
주문 데이터는 목록은 추후 확인해보도록 하고 더미데이터로 생성한 user와 product 테이블 레코드를 확인해보자.
docker
환경에서 데이터베이스 접근더미데이터가 잘 생성되었는지 데이터베이스에서 확인해보자. 물론 mysql workbench를 통해서 바로 알아볼 수 있긴 하지만 터미널을 통해서도 알아볼 수 있다.
이때, 도커환경이라는 점을 고려해서 몇가지 수행 절차를 밟아야하는데 이 역시 아주 간단하다.
C:\Users\ASUS\Desktop\Nest.JS\nest-ambassador>docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d55c49c28b62 nest-ambassador-backend "docker-entrypoint.s…" 7 days ago Up 7 days 0.0.0.0:8000->3400/tcp nest-ambassador-backend-1
a05d06fa807b mysql:8.0.29 "docker-entrypoint.s…" 7 days ago Up 7 days 33060/tcp, 0.0.0.0:33066->3306/tcp ✔nest-ambassador-db-1
5e1196a22bd6 redis "docker-entrypoint.s…" 7 days ago Up 7 days 0.0.0.0:6379->6379/tcp nest-ambassador-redis-1
C:\Users\ASUS\Desktop\Nest.JS\nest-ambassador>docker exec -it nest-ambassador-db-1 bash
bash-4.4# mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 200
Server version: 8.0.29 MySQL Community Server - GPL
Copyright (c) 2000, 2022, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
현재 실행중인 디렉토리 경로에서 입력된 컨테이너 NAMES에 해당하는 컨테이너를 실행시켜주면 된다. 그렇게 우린 도커를 통해 빌드된 mysql 컨테이너에 접근할 수 있게 된다.
✔ User 데이터 확인하기 (더미데이터를 통한 ambassador 추가)
참고로 본인이 직접 회원가입/로그인 프로세스를 거쳐서 생성한 (예를들면 a,b,c ...의 값을 가지는 데이터의 경우) 유저 데이터와 더미 데이터가 섞여있습니다. 양해바랍니다.
여기서 중요한 것은 is_ambassador 필드의 값이 0이나 1이냐 입니다.
mysql> SELECT*FROM ambassador.users;
+----+------------+-------------+------------------------------------+--------------------------------------------------------------+---------------+--------------------------------------------------------------+------------------------+
| id | first_name | last_name | email | password | is_ambassador | currentRefreshToken | currentRefreshTokenExp |
+----+------------+-------------+------------------------------------+--------------------------------------------------------------+---------------+--------------------------------------------------------------+------------------------+
| 1 | a | a | a@a.com | $2a$12$fsob7o/eY5xx9jeT0iJnJ.8pRDzRLfAIjRkiQynFVgWB6itmj/gKG | 0 | $2a$10$KY2HsZKOhzL6iZEwmFADiu3xsuDtMo6gYaavwVMSvw6zpMWGVcJk. | 2023-07-02 13:17:20 |
| 2 | b | b | b@b.com | $2a$12$Mpq0BWuuiNc99XAgXmDxouCPGcROcykeLPeLVXNQUgwZqQWITOLtq | 0 | NULL | NULL |
| 3 | c | c | c@c.com | $2a$12$g8/5k8TW0pZcnzBZsY5Se.m1Qtqj4OFUtLtGabCB.MphOOvCvR5uC | 0 | NULL | NULL |
| 4 | Fritz | Donnelly | Dimitri_Littel-Kovacek@hotmail.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 5 | Henri | Torp | Amiya.Smith67@yahoo.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 6 | April | Jacobi | Erna23@hotmail.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 7 | Helmer | Daniel | Gaston.Stroman@hotmail.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 8 | Aleen | Reinger | Rozella49@gmail.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 9 | Colt | Considine | Darryl66@gmail.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 10 | Alexandre | Bechtelar | Lorine.Hammes@gmail.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 11 | Hugh | Johnston | Irma14@hotmail.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 12 | Cali | Berge | Barbara.Wilkinson@yahoo.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 13 | Brice | Yundt | Elton_Littel@yahoo.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 14 | Guido | Price | Jayson.Lowe21@hotmail.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 15 | Janick | Roob | Sigrid.Bayer@yahoo.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 16 | Aliyah | Brakus | Richie.Sawayn4@gmail.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 17 | Junius | Senger | Therese55@yahoo.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 18 | Noble | Graham | Lyda.Lakin@yahoo.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 19 | Reggie | Jerde | Lucious_Stehr@hotmail.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 20 | Melvina | Welch | Heather41@gmail.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 21 | Germaine | Runte | Wellington21@yahoo.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 22 | Eloisa | Oberbrunner | Joe.Lueilwitz6@gmail.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 23 | Kobe | Muller | Felicita81@hotmail.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 24 | Eden | Reichert | Elza.Lowe@gmail.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 25 | Theresa | Parker | Austin_Lebsack@yahoo.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 26 | Brando | Kulas | Dudley_Bradtke29@gmail.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 27 | Cordia | Nader | Myron.Kulas@gmail.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 28 | Graciela | Conroy | Benjamin30@gmail.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 29 | Berenice | Ortiz | Nathen20@yahoo.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 30 | Demarco | Kozey | Freeman_Quitzon@gmail.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 31 | Ezequiel | Hudson | Patrick.Predovic66@yahoo.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 32 | Houston | Thompson | Katharina60@yahoo.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 33 | Albert | Spencer | Bernardo.Franecki@gmail.com | $2a$12$Vit5QUl5iKmi5dj/Gg6C7u8IqfJjOR8eEC56/aTGVJnUc6xoUBBki | 1 | NULL | NULL |
| 34 | d | d | d@d.com | $2a$12$ku/tBqeZ78NEcZCSE5t2dORo.P2sqTSkBLgKCfRZKSnJwxOXlr12a | 0 | NULL | NULL |
| 35 | e | e | e@e.com | $2a$12$F4OEWAcEIw8u9r0NBg8ZaeLrHIYOke2IS/nOzKz87CorjNKa9F8uu | 1 | $2a$10$g1T3U2csOvmp5paxziYzi.KtPg/Ki8lJ/iFUtZa7XWo4b8NmWHU.q | 2023-07-06 17:55:25 |
| 36 | f | f | f@f.com | $2a$12$OtpmXwplTVKAPfFh7MF8AeiDjgGhgKt0D//mGY5Kkhs6hRLofgXsm | 0 | $2a$10$5KLfU2ehU2PdRsAKCL2Egu66wrvvZWfjglOQEeK2QNesLtuHDheNO | 2023-07-06 16:22:36 |
| 37 | g | g | g@g.com | $2a$12$6rj4eVTFW8TMp2yHeQpO/.MvPy9yovR2GGabgcnjcatNox9iBbCi6 | 1 | $2a$10$uEMlnDzXJVA/hIgTxIZk7OA5iequ0CxMh.lXN.oXFceMOzZDceWki | 2023-07-06 17:52:54 |
+----+------------+-------------+------------------------------------+--------------------------------------------------------------+---------------+--------------------------------------------------------------+------------------------+
37 rows in set (0.00 sec)
✔ Product 데이터 확인하기
mysql> SELECT*FROM ambassador.product;
+----+-----------------------+----------------------------------------------------------------------------------------------+-------------------------------------------------------+-------+
| id | title | description | image | price |
+----+-----------------------+----------------------------------------------------------------------------------------------+-------------------------------------------------------+-------+
| 2 | title | desc 2 | https://loremflickr.com/200/200?lock=4730560809271296 | 20 |
| 3 | repellendus est | possimus cupiditate expedita delectus ullam quisquam animi aperiam iure molestias | https://picsum.photos/seed/kCLrGh/200/200 | 44 |
| 4 | facilis quos | architecto officiis officiis omnis libero fugiat minima voluptatum eligendi sed | https://loremflickr.com/200/200?lock=70498964733952 | 37 |
| 5 | ea autem | iusto excepturi debitis libero neque nesciunt quam dolore nisi illum | https://loremflickr.com/200/200?lock=2189112083742720 | 76 |
| 6 | fugit quod | quod omnis modi quas officia perspiciatis ipsam eligendi magni nemo | https://picsum.photos/seed/uH8iz95x/200/200 | 16 |
| 7 | minus deserunt | esse necessitatibus eligendi in similique tempora numquam accusantium accusamus cumque | https://loremflickr.com/200/200?lock=7717237471313920 | 76 |
| 8 | eligendi perspiciatis | magni commodi laborum accusantium consequuntur nam minima nam nam ullam | https://picsum.photos/seed/pmfOTq5I0/200/200 | 88 |
| 9 | blanditiis vero | totam voluptate exercitationem eveniet qui aperiam aperiam nulla dolore quidem | https://loremflickr.com/200/200?lock=1475789767835648 | 84 |
| 10 | eaque ad | occaecati tenetur officiis iusto dignissimos cumque nam consequatur non quis | https://picsum.photos/seed/Z3uhX3xQtq/200/200 | 26 |
| 11 | beatae modi | eius minus tempora tempore sit dicta officiis suscipit necessitatibus eum | https://loremflickr.com/200/200?lock=7595707689074688 | 77 |
| 12 | ea recusandae | magni quisquam dicta velit ipsa id nobis consectetur rerum sequi | https://loremflickr.com/200/200?lock=3169077566636032 | 19 |
| 13 | tempora tempora | magnam libero aut earum beatae nostrum debitis veritatis fugiat voluptate | https://picsum.photos/seed/B7Wk5/200/200 | 93 |
| 14 | voluptatum numquam | facere est repellendus quasi laudantium dolorem ipsum aperiam consectetur maxime | https://loremflickr.com/200/200?lock=2880736291979264 | 83 |
| 15 | dignissimos quis | omnis incidunt hic labore eligendi id cupiditate eos velit consequuntur | https://picsum.photos/seed/wPFIRxWW/200/200 | 10 |
| 16 | aut temporibus | exercitationem alias voluptate fugit tempora voluptatem blanditiis cum ipsum eligendi | https://loremflickr.com/200/200?lock=5041792160366592 | 96 |
| 17 | neque eos | asperiores natus quasi enim nemo tempora ut quas repellat porro | https://loremflickr.com/200/200?lock=6906867104088064 | 61 |
| 18 | consectetur laborum | nemo pariatur quia quas provident eveniet quibusdam eius earum natus | https://loremflickr.com/200/200?lock=4125805881851904 | 97 |
| 19 | molestiae pariatur | unde nisi repudiandae saepe consectetur dolores voluptas eius labore reprehenderit | https://picsum.photos/seed/koMSAit1/200/200 | 77 |
| 20 | similique inventore | numquam fuga optio ab maiores itaque ipsa odio rem officiis | https://loremflickr.com/200/200?lock=3596592951066624 | 87 |
| 21 | sequi voluptatibus | facilis omnis exercitationem repellendus rerum soluta voluptatum doloremque labore quibusdam | https://loremflickr.com/200/200?lock=7970722114699264 | 92 |
| 22 | libero consequatur | quidem aliquam placeat cupiditate error corrupti rerum voluptates impedit distinctio | https://loremflickr.com/200/200?lock=8328538599981056 | 60 |
| 23 | blanditiis voluptate | odit cum ipsum aspernatur officiis iste veniam suscipit perferendis aspernatur | https://picsum.photos/seed/BnUKk/200/200 | 85 |
| 24 | voluptatem voluptates | odio accusamus natus odio voluptatibus minus impedit saepe unde eligendi | https://picsum.photos/seed/iLc8bXe/200/200 | 84 |
| 25 | animi vel | deleniti voluptatum velit facilis voluptatum suscipit nihil placeat esse nisi | https://picsum.photos/seed/MZJbogz/200/200 | 73 |
| 26 | officia ratione | nulla quam excepturi adipisci dolores numquam facilis beatae odio aperiam | https://loremflickr.com/200/200?lock=4406963767083008 | 98 |
| 27 | velit deserunt | aperiam expedita ipsam quos suscipit accusamus magni at temporibus atque | https://picsum.photos/seed/2x7v4/200/200 | 23 |
| 28 | accusamus adipisci | eligendi praesentium nostrum voluptatum cumque laborum debitis velit mollitia sit | https://loremflickr.com/200/200?lock=5286963041009664 | 59 |
| 29 | praesentium adipisci | aliquid nam asperiores consequuntur iusto fugit deleniti dignissimos quo adipisci | https://loremflickr.com/200/200?lock=7432273158733824 | 18 |
| 30 | facilis praesentium | temporibus non numquam molestias beatae nulla amet provident dolore eaque | https://loremflickr.com/200/200?lock=4508827791654912 | 96 |
| 31 | totam eligendi | ut provident incidunt incidunt nisi ipsa expedita enim velit harum | https://loremflickr.com/200/200?lock=4730560809271296 | 26 |
+----+-----------------------+----------------------------------------------------------------------------------------------+-------------------------------------------------------+-------+
30 rows in set (0.01 sec)
Link
구현부 검증✔ ambassador
로그인
먼저, link를 생성하고자 하는 유저는 is_ambassador
가 true인 유저여야한다. 테스트시 꼭 ambassador endpoint로 로그인 시켜주자.
✔ link 생성하기
이것이 무엇을 의미하느냐?
35번 id값을 지닌 유저(ambassador인 유저)가 판매 대리인으로써의 역할을 수행하고 동시에 7, 10번의 id값에 해당하는 product를 상품으로써 가진다는 것을 의미한다. 그리고 생성된 코드값 code
는 추후 주문 시 꼭 필요한 데이터로 쓰일 것이다.
✔ link를 통한 판매 통계 알아보기
이 글을 쓰기 전 이미 몇가지 테스트를 해본 상태이므로 현재 수익이 찍혀있고, 주문 결제가 완료된 데이터가 있는 것은 양해부탁 드립니다.
첫 번째 통계 데이터로 나온 code:euaydmr
의 경우 이미 주문을 수행한 경우이다. 그리고 아래의 code:hl4oh8
의 경우가 바로 위에서 생성한 새로운 link 데이터에 해당한다. 아직 코드만 생성이되있고 상품 갯수, 수익은 발생되지 않은 상태이다.
✔ 중간 매핑 테이블(link_products) 확인해보기
mysql> SELECT*FROM ambassador.link_products;
+----------------+-----------+--------+
| id | productId | linkId |
+----------------+-----------+--------+
| 00000010000002 | 2 | 1 |
| 00000010000005 | 5 | 1 |
| 00000020000007 | 7 | 2 |
| 00000020000010 | 10 | 2 |
+----------------+-----------+--------+
4 rows in set (0.01 sec)
Order
구현부 검증 + Stripe
검증바로 앞전 포스팅에서 결제 주문 api를 생성하는 과정에서 바디의 Request Dto로 createOrderDto
객체를 준수하게끔 하였다. 해당 요청 객체를 준수하여 바디(전문)를 작성하면 아래와 같다.
POST | http://localhost:8000/api/checkout/orders
// body _createOrderDto
{
"first_name": "Fritz",
"last_name": "Donnelly",
"email": "Dimitri_Littel-Kovacek@hotmail.com",
"address": "ad",
"country": "korea",
"city": "seoul",
"zip": "123456",
"code": "hl4oh8", // --> 해당 코드는 앞서 생성한 링크를 통해 받아온 코드값이다.
"products": [
{
"products_id": 7,
"quantity": 2
},
{
"products_id": 10,
"quantity": 3
}
]
}
위의 요청에 대한 응답을 확인해보자. 만약 위의 요청 형식에 어긋나는 혹은 틀린 정보가 기입되었다면, 결제 주문 생성에 대한 트랜잭션 처리중의 에러로 빠지게 될 것이고 Invalid Request Exception을 던질 것이다.
// response
{
// transaction_id === sourceId
"id": "cs_test_b1CJeeE9dMxRo1kWD26imUjR9vaFhVWB4K3fApgve432aMXBvNT387jTpW",
"object": "checkout.session",
"after_expiration": null,
"allow_promotion_codes": null,
"amount_subtotal": 23000,
"amount_total": 23000,
"automatic_tax": {
"enabled": false,
"status": null
},
"billing_address_collection": null,
"cancel_url": "http://localhost:5000/error",
"client_reference_id": null,
"consent": null,
"consent_collection": null,
"created": 1688016792,
"currency": "usd",
"currency_conversion": null,
"custom_fields": [],
"custom_text": {
"shipping_address": null,
"submit": null
},
"customer": null,
"customer_creation": "if_required",
"customer_details": null,
"customer_email": null,
"expires_at": 1688103192,
"invoice": null,
"invoice_creation": {
"enabled": false,
"invoice_data": {
"account_tax_ids": null,
"custom_fields": null,
"description": null,
"footer": null,
"metadata": {},
"rendering_options": null
}
},
"livemode": false,
"locale": null,
"metadata": {},
"mode": "payment",
"payment_intent": null,
"payment_link": null,
"payment_method_collection": "always",
"payment_method_options": {},
"payment_method_types": [
"card"
],
"payment_status": "unpaid",
"phone_number_collection": {
"enabled": false
},
"recovered_from": null,
"setup_intent": null,
"shipping_address_collection": null,
"shipping_cost": null,
"shipping_details": null,
"shipping_options": [],
"status": "open",
"submit_type": null,
"subscription": null,
"success_url": "http://localhost:5000/success?source={CHECK_SESSION_ID}",
"total_details": {
"amount_discount": 0,
"amount_shipping": 0,
"amount_tax": 0
},
"url": "https://checkout.stripe.com/c/pay/cs_test_b1CJeeE9dMxRo1kWD26imUjR9vaFhVWB4K3fApgve432aMXBvNT387jTpW#fidkdWxOYHwnPyd1blpxYHZxWjA0S0lsS2hCZnRdRGtwPTJDM3FOdUBqPX1LRH10cnZCbGRIaEt2aVJuVG03aHFcPTFOXDx9ZmFNVEcyc0tuRDV0QX98YUFhSDJKfTFCSnd8THNxSUg1dVVMNTVdakBmdUxHUycpJ2N3amhWYHdzYHcnP3F3cGApJ2lkfGpwcVF8dWAnPydocGlxbFpscWBoJyknYGtkZ2lgVWlkZmBtamlhYHd2Jz9xd3BgeCUl"
}
Stripe를 통한 응답으로 많은 정보들이 출력된 것을 확인할 수 있다. 응답 객체의 모든 속성들에 대해 알아보진 않겠다. 아마 실제 결제가 아닌 테스트 환경이므로 대부분의 속성값들이 null일 것으로 예상한다.
우리가 사용할 부분은 가장 위의 id
값, 즉 "transaction_id" 값이다. 이것은 꼭 기억해두자.
가장 마지막 응답 속성으로 url
주소가 나온것을 확인할 수 있다. 이곳으로 한번 이동해보자.
결제창으로 이동한 것을 확인할 수 있다!!!
또한 결제창에서 확인할 수 있듯이 전체 가격(US $)과 주문 상품 이미지, title, description, 갯수(quantity)모두 우리가 링크 생성과 주문을 통해 요청한 그대로 반영된 것을 확인할 수 있다.
첫 번째 상품 가격: 76$ * 2(quantity)
두 번째 상품 가격: 26$ * 3(quantity)
mysql> SELECT*FROM ambassador.product WHERE id IN(7,10);
+----+----------------+----------------------------------------------------------------------------------------+-------------------------------------------------------+-------+
| id | title | description | image | price |
+----+----------------+----------------------------------------------------------------------------------------+-------------------------------------------------------+-------+
| 7 | minus deserunt | esse necessitatibus eligendi in similique tempora numquam accusantium accusamus cumque | https://loremflickr.com/200/200?lock=7717237471313920 | 76 |
| 10 | eaque ad | occaecati tenetur officiis iusto dignissimos cumque nam consequatur non quis | https://picsum.photos/seed/Z3uhX3xQtq/200/200 | 26 |
+----+----------------+----------------------------------------------------------------------------------------+-------------------------------------------------------+-------+
2 rows in set (0.00 sec)
orders 테이블에 아래와 같이 주문 생성이 반영된 것을 확인할 수 있다. 하지만 결제 확인절차를 아직 밟지 않았으므로 complete의 상태는 "0"이다.
(사실 id=1이 아니지만 임의로 1이라 가정한다)
mysql> SELECT*FROM ambassador.orders WHERE id=1;
+----+--------------------------------------------------------------------+---------+--------+------------------+------------+-----------+------------------------------------+---------+---------+-------+--------+----------+
| id | transaction_id | user_id | code | ambassador_email | first_name | last_name | email | address | country | city | zip | complete |
+----+--------------------------------------------------------------------+---------+--------+------------------+------------+-----------+------------------------------------+---------+---------+-------+--------+----------+
| 1 | cs_test_b1CJeeE9dMxRo1kWD26imUjR9vaFhVWB4K3fApgve432aMXBvNT387jTpW | 35 | hl4oh8 | e@e.com | Fritz | Donnelly | Dimitri_Littel-Kovacek@hotmail.com | ad | korea | seoul | 123456 | 0 |
+----+--------------------------------------------------------------------+---------+--------+------------------+------------+-----------+------------------------------------+---------+---------+-------+--------+----------+
1 row in set (0.00 sec)
자, 그럼 확인절차를 밟아보자.
잠깐 결제확인(confirm) 라우트 핸들러부를 다시 살펴보면 아래와 같이 transaction_id
즉, sourceId를 통해 요청을 찌르는것을 확인할 수 있다.
@Post('checkout/orders/confirm')
async confirm(
@Body('sourceId') sourceId: string,
) {
return await this.orderService.orderConfirm(sourceId);
}
아래의 요청 경로로 이동해
http://localhost:8000/api/checkout/orders/confirm/
바디(전문)에 우리가 앞서 결제 생성시 발급받은 transaction_id
값을 실어준다.
결제 확인이 잘 수행되었다.
또한 orders 테이블을 보면, 결제 확인 전 false 상태였던 complete 값이 true로 변경된 것 또한 확인할 수 있다.
mysql> SELECT*FROM ambassador.orders WHERE id=1;
+----+--------------------------------------------------------------------+---------+--------+------------------+------------+-----------+------------------------------------+---------+---------+-------+--------+----------+
| id | transaction_id | user_id | code | ambassador_email | first_name | last_name | email | address | country | city | zip | complete |
+----+--------------------------------------------------------------------+---------+--------+------------------+------------+-----------+------------------------------------+---------+---------+-------+--------+----------+
| 1 | cs_test_b1CJeeE9dMxRo1kWD26imUjR9vaFhVWB4K3fApgve432aMXBvNT387jTpW | 35 | hl4oh8 | e@e.com | Fritz | Donnelly | Dimitri_Littel-Kovacek@hotmail.com | ad | korea | seoul | 123456 | 1 |
+----+--------------------------------------------------------------------+---------+--------+------------------+------------+-----------+------------------------------------+---------+---------+-------+--------+----------+
1 row in set (0.00 sec)
다음포스팅에선 이번 포스팅까지 알아본 결제 프로세스를 바탕으로 레디스의 "Sorted Set"을 활용하여 랭킹 기능을 구현해보도록 하자.
생각정리는 생략하겠다...