[๐Ÿ‘ฉโ€๐Ÿ’ป] ์•„์ž„ํฌํŠธ ์—ฐ๋™

Katherine12ยท2022๋…„ 9์›” 5์ผ
0

์›Œ์›Œ์›ค

๋ชฉ๋ก ๋ณด๊ธฐ
1/1
post-thumbnail
post-custom-banner

์•„์ž„ํฌํŠธ ์—ฐ๊ฒฐ ํŽ˜์ด์ง€

์„œ๋ฒ„๋‹จ ์—ฐ๊ฒฐํ•˜๊ธฐ ์ „ PUG๋กœ ๊ฐ„๋‹จํ•˜๊ฒŒ ์•„์ž„ํฌํŠธ๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ํŽ˜์ด์ง€์™€ ์—๋Ÿฌ๋ฅผ ๋ฐ›๋Š” ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

  • index.pug
<!-- jQuery -->
script(src="https://code.jquery.com/jquery-3.5.1.min.js")
<!-- iamport.payment.js -->
script(src="https://cdn.iamport.kr/js/iamport.payment-1.1.7.js")

<!-- Script ๋ถ€๋ถ„ -->
const data = {
   pg : "#{pg}",
   pay_method : "#{pay_method}",
   name : "#{name}",
   merchant_uid : "#{merchant_uid}",
   amount : "#{amount}",
   buyer_name : "#{buyer_name}",
   buyer_tel : "#{buyer_tel}",
   buyer_email : "#{buyer_email}",
   buyer_addr : "#{buyer_addr}",
   buyer_postcode : "#{buyer_postcode}",
   notice_url: "#{webhook}"
};
const IMP = window.IMP;
IMP.init("๊ฐ€๋งน์  ์‹๋ณ„์ฝ”๋“œ");
IMP.request_pay(data,(response)=>{
   $.post("#{callbackUrl}/API ์ฃผ์†Œ",response,
   function(res){
      ReactNativeWebView.postMessage(JSON.stringify(res));
     });
   });
  • error.pug
<!-- jQuery -->
script(src="https://code.jquery.com/jquery-3.5.1.min.js")
<!-- iamport.payment.js -->
script(src="https://cdn.iamport.kr/js/iamport.payment-1.1.7.js")
<!-- Script๋ถ€๋ถ„ -->
ReactNativeWebView.postMessage(JSON.stringify({result: #{result}}));

pug ๊ธฐ๋ณธ ๋ฌธ๋ฒ•์€ #{}๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์•„์ž„ํฌํŠธ ํ‚ค

RESPAPI KEY
์•„์ž„ํฌํŠธ ์–ด๋“œ๋ฏผ์— ๋กœ๊ทธ์ธ ํ•œ ๋’ค ๊ฒฐ์ œ ์—ฐ๋™- ์šฐ์ธก ์ƒ๋‹จ์— ๋‚ด ์‹๋ณ„์ฝ”๋“œ.API Keys์— ๋“ค์–ด๊ฐ€์‹œ๋ฉด ๊ฐ€๋งน์  ์‹๋ณ„์ฝ”๋“œ, REST API KEY, REST API Secret ๊ฐ’์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
ํ…Œ์ŠคํŠธ ์—ฐ๋™
๋˜ํ•œ, ๊ฒฐ์ œ ์—ฐ๋™ ํŽ˜์ด์ง€ ํ•˜๋‹จ์— ๋ณด์‹œ๋ฉด ํ…Œ์ŠคํŠธ ์—ฐ๋™์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์„ค์ •ํ•œ PG์‚ฌ์˜ ์ •๋ณด๋ฅผ ํ™•์ธ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

์„œ๋ฒ„์ž‘์—…

  • access token
    access token์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ฐœํ–‰ ์‹œ๊ฐ„ ํ›„ 30๋ถ„๋™์•ˆ ์œ ํšจํ•˜๋ฉฐ ๋งŒ๋ฃŒ ์‹œ๊ฐ„ 1๋ถ„์ „ ์š”์ฒญ ์‹œ 5๋ถ„์ด ๋” ์—ฐ์žฅ๋ฉ๋‹ˆ๋‹ค.
cosnt get_token= async=> {
	const access_token = await axios({
    	url: "https://api.iamport.kr/users/getToken",
            method: "post",
            headers: { "Content-Type": "application/json" },
            data: {
                imp_key: "REST API KEY",
                imp_secret: "REST API SECRET"
            }
    });
    return access_token
}
  • ์•„์ž„ํฌํŠธ์—์„œ ๊ฒฐ์ œ ์ •๋ณด ๋ฐ›์•„์˜ค๊ธฐ
const get_imp= async(imp_uid)=> {
const access_token= await get_token();
const getPaymentData = await axios({
	url:`https://api.iamport.kr/payments/${imp_uid}`, // imp_uid ์ „๋‹ฌ
	method: "get", // GET method
	headers: { "Authorization": access_token } // ์ธ์ฆ ํ† ํฐ Authorization header์— ์ถ”๊ฐ€
});
const paymentData = getPaymentData.data.response;
        
}
  • Router์— ์—ฐ๊ฒฐ(302 redirect ๋ฐ›๋Š” ๋ผ์šฐํ„ฐ์™€ webhook๋ผ์šฐํ„ฐ)
router.post("/iamport", async(req, res)=> {
	const { success, imp_uid, merchant_uid } = req.body; // ์•„์ž„ํฌํŠธ์—์„œ body๋กœ ๋ณด๋‚ด์ฃผ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ
    // success ๊ฐ€ ์—†์„ ๋•Œ
    if(success === undefined || success === null){
    	res.json({result: false});
    }else{
    	try{
        	const {amount, status} = await get_imp(imp_uid);
            const [imp_history]= pool.query(`SELECT AMOUNT FROM ์•„์ž„ํฌํŠธ ๊ธฐ๋ก ํ…Œ์ด๋ธ” WHERE merchant_uid= merchant_uid;`); // DB์— ์ €์žฅ๋œ ๊ธฐ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ
            ...
           	amount ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธ ๋ฐ status์— ๋งž๊ฒŒ ์ฒ˜๋ฆฌ
            (status- paid(์„ฑ๊ณต), ready(์ž…๊ธˆ ์˜ˆ์ •, ๊ฐ€์ƒ ๊ณ„์ขŒ ๋ฐœ๊ธ‰), failed(์ž”์•ก ๋ถ€์กฑ ๋“ฑ์˜ ์‹คํŒจ)
            ...
            return res.json({result: true});
        }catch(error){
        	console.log(error);
        }
    }

});
  • Webhook Router
router.post("/webhook", async(req, res)=> {
	const { imp_uid, merchant_uid } = req.body; // ์•„์ž„ํฌํŠธ์—์„œ body๋กœ ๋ณด๋‚ด์ฃผ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ
    try{
    const {amount, status} = await get_imp(imp_uid);
            const [imp_history]= pool.query(`SELECT AMOUNT FROM ์•„์ž„ํฌํŠธ ๊ธฐ๋ก ํ…Œ์ด๋ธ” WHERE merchant_uid= merchant_uid;`); // DB์— ์ €์žฅ๋œ ๊ธฐ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ
            ...
           	amount ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธ ๋ฐ status์— ๋งž๊ฒŒ ์ฒ˜๋ฆฌ
            (status- paid(์„ฑ๊ณต), ready(์ž…๊ธˆ ์˜ˆ์ •, ๊ฐ€์ƒ ๊ณ„์ขŒ ๋ฐœ๊ธ‰), failed(์ž”์•ก ๋ถ€์กฑ ๋“ฑ์˜ ์‹คํŒจ)
            ...
            return res.json({result: true});
        }catch(error){
        	console.log(error);
        }
    }

});

webhook๊ณผ redirect ์‹œ ๋ฐ›๋Š” ๋ผ์šฐํ„ฐ์˜ ๋กœ์ง์€ ์ƒ๋‹น๋ถ€๋ถ„ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์„ ๋‘๋ฒˆ ๊ฑฐ์น˜๋Š” ์ด์œ ๋Š” ๋„คํŠธ์›Œํฌ์˜ ๋ฌธ์ œ๋กœ ์ค‘๊ฐ„์— ์œ ์‹ค ๋  ๊ฒฝ์šฐ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ณ ์ž webhook ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
๊ทธ๋ ‡๋‹ค๋ฉด redirect์šฉ ๋ผ์šฐํ„ฐ์™€ webhook ๋ผ์šฐํ„ฐ ์ˆœ์„œ๋Š” ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”?
์ •๋‹ต์€ ์ˆœ์„œ๋Š” ์ง€์ •๋˜์–ด ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ง€์ •์„ ์›ํ•˜์‹œ๋ฉด ์•„์ž„ํฌํŠธ์ธก์— ์š”์ฒญํ•˜์‹œ๋ฉด webhook - redirect ์ˆœ์œผ๋กœ ์ง€์ •์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
์ €๋Š” ๋”ฐ๋กœ ์ง€์ •ํ•˜์ง€ ์•Š๊ณ  ์ง„ํ–‰ํ–ˆ์œผ๋ฉฐ DB์— ์•„์ž„ํฌํŠธ์—์„œ ๋ณด๋‚ด์ฃผ๋Š” status๊ฐ’์„ ์ €์žฅํ•˜์—ฌ status๊ฐ€ null์ธ๊ฒฝ์šฐ ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์•„๋‹Œ ๊ฒฝ์šฐ ๋กœ์ง์„ ํƒ€์ง€ ์•Š๊ณ  else๋ฌธ์œผ๋กœ ๋‚˜์˜ค๋„๋ก ์ฒ˜๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.

WEBHOOK

Webhook์„ ๋ณด๋‚ด๊ธฐ ์ „ ์„ค์น˜ํ•  ๊ฒƒ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
๊ทธ๊ฑด ๋ฐ”๋กœ ngrok์ž…๋‹ˆ๋‹ค.
ngrok ์‹คํ–‰ํ™”๋ฉด
ngrok์—์„œ ๊ฐ ๋ฒ„์ „์— ๋งž๊ฒŒ ๋‹ค์šด๋กœ๋“œ ํ•˜์‹  ํ›„ exeํŒŒ์ผ์„ ์‹คํ–‰ํ•˜์‹œ๋ฉด ์œ„ ์ด๋ฏธ์ง€์ฒ˜๋Ÿผ ๋œจ๊ฒŒ๋ฉ๋‹ˆ๋‹ค.
์ปค๋ฉ˜๋“œ์—์„œ ngrok http localhost:3000 ์น˜์‹œ๋ฉด ๋ฐ”๋กœ ์—ฐ๋™์ด ๋˜๋ฉฐ, webhook ํ˜ธ์ถœ ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
ngrok ip
index.pug์—์„œ ๋กœ์ปฌ์—์„œ webhook ํ…Œ์ŠคํŠธ๋ฅผ ํ•  ์‹œ์— notice_url์— webhook ์ปค๋ฉ˜๋“œ์— ๋– ์žˆ๋Š” Forwarding์˜ ์ฃผ์†Œ๋ฅผ ๋ถ™์ธ ํ›„ api๊นŒ์ง€ ๋ถ™์ด๋ฉด webhook ์—ฐ๋™์€ ๋์ž…๋‹ˆ๋‹ค.(ex.https://${address}.${region}.ngrok.io/API)


API ๋ฌธ์„œ

์ผ๋ฐ˜๊ฒฐ์ œ
Webhook
API ์•„์ž„ํฌํŠธ
javascript-sdk

profile
Node.js ์™€ ์นœํ•ด์ง€๊ณ  ์žˆ๋Š” ์„œ๋ฒ„๊ฐœ๋ฐœ์ž์ž…๋‹ˆ๋‹ค :)
post-custom-banner

0๊ฐœ์˜ ๋Œ“๊ธ€