์๋ฒ๋จ ์ฐ๊ฒฐํ๊ธฐ ์ 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));
});
});
<!-- 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 ๊ธฐ๋ณธ ๋ฌธ๋ฒ์ #{}๋ก ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌ ๋ฐ์ ์ ์์ต๋๋ค.
์์ํฌํธ ์ด๋๋ฏผ์ ๋ก๊ทธ์ธ ํ ๋ค ๊ฒฐ์ ์ฐ๋- ์ฐ์ธก ์๋จ์ ๋ด ์๋ณ์ฝ๋.API Keys์ ๋ค์ด๊ฐ์๋ฉด ๊ฐ๋งน์ ์๋ณ์ฝ๋, REST API KEY, REST API Secret ๊ฐ์ ํ์ธ ํ ์ ์์ต๋๋ค.
๋ํ, ๊ฒฐ์ ์ฐ๋ ํ์ด์ง ํ๋จ์ ๋ณด์๋ฉด ํ
์คํธ ์ฐ๋์ด ์์ต๋๋ค. ์ค์ ํ PG์ฌ์ ์ ๋ณด๋ฅผ ํ์ธ ๊ฐ๋ฅํฉ๋๋ค.
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.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);
}
}
});
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์ ๋ณด๋ด๊ธฐ ์ ์ค์นํ ๊ฒ์ด ์์ต๋๋ค.
๊ทธ๊ฑด ๋ฐ๋ก ngrok์
๋๋ค.
ngrok์์ ๊ฐ ๋ฒ์ ์ ๋ง๊ฒ ๋ค์ด๋ก๋ ํ์ ํ exeํ์ผ์ ์คํํ์๋ฉด ์ ์ด๋ฏธ์ง์ฒ๋ผ ๋จ๊ฒ๋ฉ๋๋ค.
์ปค๋ฉ๋์์ ngrok http localhost:3000
์น์๋ฉด ๋ฐ๋ก ์ฐ๋์ด ๋๋ฉฐ, webhook ํธ์ถ ํ
์คํธ๊ฐ ๊ฐ๋ฅํฉ๋๋ค.
index.pug์์ ๋ก์ปฌ์์ webhook ํ
์คํธ๋ฅผ ํ ์์ notice_url์ webhook ์ปค๋ฉ๋์ ๋ ์๋ Forwarding์ ์ฃผ์๋ฅผ ๋ถ์ธ ํ api๊น์ง ๋ถ์ด๋ฉด webhook ์ฐ๋์ ๋์
๋๋ค.(ex.https://${address}.${region}.ngrok.io/API
)