webhook 1transaction - nicepayments/nicepay-manual GitHub Wiki
1 Transaction ๋ฐฉ์์์ ์นํ ๊ณผ ์น์ธ๊ธ์ก๊ฒ์ฆ API๋ฅผ ํ์ฉํ ์์ ์ ์ธ ๊ฒฐ์ ์ฒ๋ฆฌ ๊ฐ์ด๋
1 Transaction ๋ฐฉ์์ NICEPAY๊ฐ ์ธ์ฆ๊ณผ ์น์ธ์ ๋ชจ๋ ์ฒ๋ฆฌํ๊ณ returnUrl๋ก ๊ฒฐ๊ณผ๋ฅผ ์ ๋ฌํฉ๋๋ค.
๊ณ ๊ฐ ๊ฒฐ์ โ NICEPAY ์ธ์ฆ+์น์ธ โ returnUrl ์๋ต (ํ์์์ ๋ฐ์!)
๋คํธ์ํฌ ํ์์์ ์:
- ๊ฒฐ์ ๋ ์ฑ๊ณตํ์ง๋ง ๊ฐ๋งน์ ์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ์ง ๋ชปํจ
- ๊ณ ๊ฐ์ ๊ฒฐ์ ์๋ฃ ํ์ด์ง๋ฅผ ๋ณด์ง ๋ชปํจ
- ์ฃผ๋ฌธ ์ํ๊ฐ ๋ถํ์คํ ์ํ๋ก ๋จ์
์นํ ์ ๋ฐฑ์ ํ๋ก์ฐ๋ก ์ฌ์ฉํ์ฌ ์์ ์ฑ์ ํ๋ณดํฉ๋๋ค.
๊ณ ๊ฐ ๊ฒฐ์ โ NICEPAY ์ธ์ฆ+์น์ธ โ โฌโ returnUrl (๋ฉ์ธ)
โโ ์นํ
(๋ฐฑ์
)
์ฅ์ :
- โ returnUrl ํ์์์ ์์๋ ์นํ ์ผ๋ก ๊ฒฐ๊ณผ ์์
- โ ๊ฒฐ์ ๋๋ฝ ๋ฐฉ์ง
- โ ์์ ์ ์ธ ์ฃผ๋ฌธ ์ฒ๋ฆฌ
returnUrl๋ก ๋ฐ์ ๊ฒฐ์ ๊ฒฐ๊ณผ๊ฐ ์๋ณ์กฐ๋์ง ์์๋์ง ๊ฒ์ฆํฉ๋๋ค.
curl -X POST 'https://api.nicepay.co.kr/v1/payments/{tid}/amount' \
-H 'Content-type: application/json' \
-H 'Authorization: Basic UjJfeW91cl9jbGllbnRfaWQ6...' \
-d '{
"amount": 10000
}'๊ธ์ก ์ผ์น ์:
{
"resultCode": "0000",
"resultMsg": "์ ์ ์ฒ๋ฆฌ๋์์ต๋๋ค.",
"tid": "ORD20240115XXXXXXXXXX",
"amount": 10000,
"isMatched": true
}๊ธ์ก ๋ถ์ผ์น ์:
{
"resultCode": "A001",
"resultMsg": "๊ธ์ก์ด ์ผ์นํ์ง ์์ต๋๋ค.",
"isMatched": false
}app.get('/return', async (req, res) => {
const { tid, orderId, amount, authResultCode } = req.query;
// 1. ๊ฒฐ์ ์ฑ๊ณต ์ฌ๋ถ ํ์ธ
if (authResultCode !== '0000') {
return res.send('๊ฒฐ์ ์คํจ');
}
// 2. ์น์ธ๊ธ์ก๊ฒ์ฆ
const isValid = await verifyAmount(tid, amount);
if (!isValid) {
console.error('โ ๊ธ์ก ๋ถ์ผ์น - ์๋ณ์กฐ ์์ฌ');
return res.send('๊ฒฐ์ ๊ฒ์ฆ ์คํจ');
}
// 3. ์ค๋ณต ์ฒ๋ฆฌ ๋ฐฉ์ง
if (await isProcessed(orderId)) {
return res.send('๊ฒฐ์ ์๋ฃ');
}
// 4. ์ฃผ๋ฌธ ์ฒ๋ฆฌ
await processOrder(orderId, tid, amount);
res.send('๊ฒฐ์ ์๋ฃ');
});app.post('/webhook/nicepay', async (req, res) => {
const webhookData = req.body;
// ์ฆ์ OK ์๋ต (ํ์)
res.status(200).type('text/html').send('OK');
// ๋ฐฑ๊ทธ๋ผ์ด๋ ์ฒ๋ฆฌ
setImmediate(async () => {
// 1. Signature ๊ฒ์ฆ
if (!verifySignature(webhookData)) {
return;
}
// 2. ์ค๋ณต ์ฒ๋ฆฌ ๋ฐฉ์ง
if (await isProcessed(webhookData.orderId)) {
return;
}
// 3. ์ฃผ๋ฌธ ์ฒ๋ฆฌ
await processOrder(
webhookData.orderId,
webhookData.tid,
webhookData.amount
);
});
});// Redis๋ฅผ ํ์ฉํ ์ค๋ณต ์ฒดํฌ
async function isProcessed(orderId) {
const key = `order:processed:${orderId}`;
const exists = await redis.exists(key);
if (!exists) {
// 1์๊ฐ ๋์ ์ฒ๋ฆฌ ์๋ฃ ์ํ ์ ์ง
await redis.setex(key, 3600, '1');
return false;
}
return true;
}1. ๊ฒฐ์ ์๋ฃ
2. returnUrl ์์ โ ์น์ธ๊ธ์ก๊ฒ์ฆ โ ์ฃผ๋ฌธ ์ฒ๋ฆฌ
3. ์นํ
์์ โ ์ค๋ณต ํ์ธ โ ๊ฑด๋๋ฐ๊ธฐ
๊ฒฐ๊ณผ: โ ์ฃผ๋ฌธ 1ํ๋ง ์ฒ๋ฆฌ
1. ๊ฒฐ์ ์๋ฃ
2. returnUrl ํ์์์ (๋คํธ์ํฌ ์ค๋ฅ)
3. ์นํ
์์ โ ์ฃผ๋ฌธ ์ฒ๋ฆฌ
๊ฒฐ๊ณผ: โ ์นํ ์ผ๋ก ์ฃผ๋ฌธ ์ฒ๋ฆฌ, ๊ฒฐ์ ๋๋ฝ ์์
1. ๊ฒฐ์ ์๋ฃ
2. returnUrl๊ณผ ์นํ
๊ฑฐ์ ๋์ ์์
3. ์ฒซ ๋ฒ์งธ ์์ฒญ์ด ์ฃผ๋ฌธ ์ฒ๋ฆฌ
4. ๋ ๋ฒ์งธ ์์ฒญ์ ์ค๋ณต ํ์ธ ํ ๊ฑด๋๋ฐ๊ธฐ
๊ฒฐ๊ณผ: โ ์ฃผ๋ฌธ 1ํ๋ง ์ฒ๋ฆฌ
1. returnUrl ํ๋ผ๋ฏธํฐ ์กฐ์ (amount ๋ณ๊ฒฝ)
2. ์น์ธ๊ธ์ก๊ฒ์ฆ API ํธ์ถ
3. ๊ธ์ก ๋ถ์ผ์น ๊ฐ์ง โ ์ฒ๋ฆฌ ์ค๋จ
๊ฒฐ๊ณผ: โ ์๋ณ์กฐ ๊ฐ์ง, ์ฃผ๋ฌธ ์ฒ๋ฆฌ ์ ๋จ
API ๋ช ์ธ
POST /v1/payments/{tid}/amount
| ํ๋ | ํ์ | ํ์ | ์ค๋ช |
|---|---|---|---|
| amount | Int | O | ๊ฒ์ฆํ ๊ธ์ก |
| ํ๋ | ํ์ | ์ค๋ช |
|---|---|---|
| resultCode | String | ๊ฒฐ๊ณผ ์ฝ๋ (0000: ์ฑ๊ณต) |
| resultMsg | String | ๊ฒฐ๊ณผ ๋ฉ์์ง |
| tid | String | ๊ฑฐ๋๋ฒํธ |
| amount | Int | ๊ฒ์ฆ ์์ฒญ ๊ธ์ก |
| isMatched | Boolean | ๊ธ์ก ์ผ์น ์ฌ๋ถ |
curl -X POST 'https://api.nicepay.co.kr/v1/payments/ORD20240115XXX/amount' \
-H 'Content-type: application/json' \
-H 'Authorization: Basic UjJfeW91cl9jbGllbnRfaWQ6...' \
-d '{
"amount": 10000
}'- returnUrl ํธ๋ค๋ฌ์์ ์น์ธ๊ธ์ก๊ฒ์ฆ API ํธ์ถ
- ์นํ ํธ๋ค๋ฌ์์ Signature ๊ฒ์ฆ
- ์ค๋ณต ์ฒ๋ฆฌ ๋ฐฉ์ง ๋ก์ง (Redis ๋๋ DB)
- returnUrl๊ณผ ์นํ ๋ชจ๋ ๋์ผํ ์ฃผ๋ฌธ ์ฒ๋ฆฌ ํจ์ ์ฌ์ฉ
- 4๊ฐ์ง ํ ์คํธ ์๋๋ฆฌ์ค ๊ฒ์ฆ
- ์ค๋ณต ์ฃผ๋ฌธ ์์ฑ ์์
- ํ์์์ ์์๋ ์ฃผ๋ฌธ ์ฒ๋ฆฌ๋จ
- ์๋ณ์กฐ ์๋ ์ฐจ๋จ
- returnUrl ํ์์์ ๋ฐ์ ๋น๋ ํ์ธ
- ์นํ ์ผ๋ก๋ง ์ฒ๋ฆฌ๋ ์ฃผ๋ฌธ ๋น์จ ํ์ธ
- ์น์ธ๊ธ์ก๊ฒ์ฆ ์คํจ ์๋ฆผ ์ค์