<script>
lines below to the payment HTML page. Since Iamport library is based on jQuery, jQuery 1.0 or higher must be included. <!-- jQuery -->
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.min.js" ></script>
<!-- iamport.payment.js -->
<script type="text/javascript" src="https://cdn.iamport.kr/js/iamport.payment-1.1.8.js"></script>
[Merchant ID](/implementation/account-info?lang=en)
into the argument of IMP
object init
method and call it from the payment page of the website. var IMP = window.IMP; // Can be skipped.
IMP.init("imp00000000"); // Replace "imp00000000" with the issued "Merchant ID".
init()
function. Check Merchant ID
in your Admin Dashboard and use it as the argument of the function.IMP.request_pay(param, callback)
. Include attributes and values that are required for the payment request, in the first argument of the function param
. // Call IMP.request_pay(param, callback)
IMP.request_pay({ // param
pg: "html5_inicis",
pay_method: "card",
merchant_uid: "ORD20180131-0000011",
name: "Norway swivel chair",
amount: 64900,
buyer_email: "johndoe@gmail.com",
buyer_name: "John Doe",
buyer_tel: "010-4242-4242",
buyer_addr: "Shinsa-dong, Gangnam-gu, Seoul",
buyer_postcode: "01181"
}, function (rsp) { // callback
if (rsp.success) {
...,
// Logic on successful payment,
...
} else {
...,
// Logic on payment failure,
...
}
});
IMP.request_pay(param, callback)
is called, the payment module window of the designated PG company appears in the PC environment. On the other hand, some PG companies provide a different process that redirects to PG’s website in a mobile environment.The first argument, param
object, contains the information required for the payment request. Specify appropriate values for the attributes, such as payment method (pay_method
), product name (name
), and the product price (amount
). Learn more about the attributes of param.Create an order number (merchant_uid)
IMP.request_pay
, it is recommended to forward order information to your server (INSERT
order information into the database) and assign the server-generated order number to the merchant_uid
attribute of param
. This is because order information must be retrieved from your server for reliable verification in the step of verifying the payment forgery after the payment is completed.
callback
, is a function executed after the customer completes the payment. The argument rsp
passed to the callback
function contains whether the payment was successful as well as the payment and error information. After the customer completes the payment process, you can check the payment result through rsp
and write the code you want to execute in the callback
. Learn more about the arguments of rsp
which are passed to the callback
.It is common to start a payment process by clicking a button on the order page. As shown in the example below, you can write code to call IMP.request_pay(param, callback)
function by clicking the payment button. <button onclick="requestPay()">Pay</button>
...
<script>
function requestPay() {
// Call IMP.request_pay(param, callback)
IMP.request_pay({ // param
pg: "html5_inicis",
pay_method: "card",
merchant_uid: "ORD20180131-0000011",
name: "Norway swivel chair",
amount: 64900,
buyer_email: "johndoe@gmail.com",
buyer_name: "John Doe",
buyer_tel: "010-4242-4242",
buyer_addr: "Shinsa-dong, Gangnam-gu, Seoul",
buyer_postcode: "01181"
}, function (rsp) { // callback
if (rsp.success) {
...,
// Logic on successful payment,
...
} else {
...,
// Logic on payment failure,
...
}
});
}
</script>
onclick
event occurs for the button, the payment process can be started by calling IMP.request_pay(param, callback)
function.rsp.success: true
) in the callback
, write a logic to pass the imp_uid
and merchant_uid
of rsp
to the server. IMP.request_pay({
/* ...code omitted here... */
}, function (rsp) { // callback
if (rsp.success) { // Successful payment: Successful payment approval or issuance of a virtual account
// HTTP request using jQuery
jQuery.ajax({
url: "https://www.myservice.com/payments/complete", // Merchant server
method: "POST",
headers: { "Content-Type": "application/json" },
data: {
imp_uid: rsp.imp_uid,
merchant_uid: rsp.merchant_uid
}
}).done(function (data) {
// Logic executed on successful response from the merchant server through the payment API
})
} else {
alert("Payment failed. Error: "+ rsp.error_msg);
}
});
imp_uid
(transaction unique number) to the merchant server, you can query the payment information from the Iamport server with imp_uid
. Also, the order information can be queried from the merchant database with merchant_uid
, which is the order number managed by the merchant. With queried information, you can verify the payment forgery and store it in the database of the service.Reasons to query the transaction information on the server side
access_token
) has to be issued with the REST API Key and REST API Secret issued from the Admin Dashboard, and then the token must be included in the API request for the payment information. When the token issuance process is performed on the client, the REST API key
and REST API Secret
are exposed which is not secure. Therefore, the process of issuing the token and querying the transaction information must be performed on the server side. imp_uid
from the server. app.use(bodyParser.json());
// Handles the POST request for "/payments/complete"
app.post("/payments/complete", async (req, res) => {
try {
const { imp_uid, merchant_uid } = req.body; // Extract imp_uid and merchant_uid from req body
} catch (e) {
res.status(400).send(e);
}
});
POST
request for /payments/complete
. imp_uid
was extracted from the HTTP request body.https:\//api.iamport.kr/users/getToken
using the [REST API Key](/implementation/account-info?lang=en)
and [REST API Secret](/implementation/account-info?lang=en)
. Then, the access token and the extracted imp_uid
are used to search for payment information through a request to https:\//api.iamport.kr/payments/$imp_uid
.REST API Access Token
app.use(bodyParser.json());
...
// Handles the POST request for "/payments/complete"
app.post("/payments/complete", async (req, res) => {
try {
const { imp_uid, merchant_uid } = req.body; // Extract imp_uid and merchant_uid from req body
...
// Acquire access token
const getToken = await axios({
url: "https://api.iamport.kr/users/getToken",
method: "post", // POST method
headers: { "Content-Type": "application/json" }, // "Content-Type": "application/json"
data: {
imp_key: "imp_apikey", // REST API key
imp_secret: "ekKoeW8RyKuT0zgaZsUtXXTLQ4AhPFW3ZGseDA6bkA5lamv9OqDMnxyeB9wqOsuO9W3Mx9YSJ4dTqJ3f" // REST API Secret
}
});
const { access_token } = getToken.data.response; // Authentication token
...
// Query payment information from Iamport with imp_uid
const getPaymentData = await axios({
url: \`https://api.iamport.kr/payments/\${imp_uid}\`, // pass imp_uid
method: "get", // GET method
headers: { "Authorization": access_token } // Append the authentication token to Authorization header
});
const paymentData = getPaymentData.data.response; // Query result
...
} catch (e) {
res.status(400).send(e);
}
});
REST API Key
and REST API Secret
issued for Iamport account to retrieve payment information. The access token request was sent to https:\//api.iamport.kr/users/getToken
using POST
method, with application/json
as Content-Type
, imp_key: REST API key, and imp_secret: REST API Secret
in the body. When the server responses to the request, it stores the access_token
, which is the authentication token included in the response data. Then, the URL https:\//api.iamport.kr/payments/{imp_uid}
was built with imp_uid
, the issued access token access_token
was added to Authorization
, and finally the payment information was queried by sending a request using GET
method.HTTP request to Iamport REST API
GET
or POST
), header(Authorization: access_token, Content-Type: application/json
), and request parameters.
You can find the details of each API in Iamport REST API documentation.If you are not familiar with HTTP requests, you can check out the link below.
Reasons to verify the payment forgery
amount: 100000
. However, an attacker can manipulate the script to set the property to a value lower than the actual amount, and then create a payment request. Since script manipulation on the client side has a technical feature that cannot be prevented by nature, the verification of payment forgery must be done on the server side after the payment.status
. app.use(bodyParser.json());
...
// Handles the POST request for "/payments/complete"
app.post("/payments/complete", async (req, res) => {
try {
const { imp_uid, merchant_uid } = req.body; // Extract imp_uid and merchant_uid from req body
// Acquire access token
/* ...code omitted here... */
// Query payment information from Iamport with imp_uid
/* ...code omitted here... */
const paymentData = getPaymentData.data.response; // Query result
...
// Query the amount to be paid from the database
const order = await Orders.findById(paymentData.merchant_uid);
const amountToBePaid = order.amount; // Amount to be paid
...
// Verify payment
const { amount, status } = paymentData;
if (amount === amountToBePaid) { // Match the payment amount payment amount === amount to be paid
await Orders.findByIdAndUpdate(merchant_uid, { $set: paymentData }); // Store the payment information in the database
...
switch (status) {
case "ready": // Issue a virtual account
// Store the virtual account issue information in database
const { vbank_num, vbank_date, vbank_name } = paymentData;
await Users.findByIdAndUpdate("/* Customer id */", { $set: { vbank_num, vbank_date, vbank_name }});
// Send text message for issuance of virtual account
SMS.send({ text: \`Virtual account has been successfully issued. Account information \${vbank_num} \${vbank_date} \${vbank_name}\`});
res.send({ status: "vbankIssued", message: "Virtual account issued successfully" });
break;
case "paid": // Payment complete
res.send({ status: "success", message: "Payment succeeded" });
break;
}
} else { // Payment amount mismatch. Payment is forged.
throw { status: "forgery", message: "Forged payment attempted" };
}
} catch (e) {
res.status(400).send(e);
}
});
merchart_uid
to find out the amount that should have been paid.
Then, the actual amount paid was compared to the amount
that should have been paid, amountToBePaid
, to check for forgery in payment. If the payment amounts are matched, the payment information is stored in the database and an appropriate response is sent based on the payment status
.
merchant_uid in database
IMP.request_pay(param, callback)
. This assumption starts with the order number management process commonly used in commerce apps.
First, when the user enters order information and tries to make a payment, the order information is delivered to the server of the store. The server stores the received order information in the store’s database, and then sends the unique order number (created when the database is saved) to the client. The client requests payment by specifying the received payment unique number in merchant_uid
of the param
attribute. A reliable verification can be done when such process is implemented.
Reasons for storing in the database of the service
IMP.request_pay({
/* ...code omitted here... */
}, function (rsp) { // callback
if (rsp.success) { // Successful payment: Successful payment approval or issuance of a virtual account
// HTTP request using jQuery
jQuery.ajax({
/* ...code omitted here... */
}).done(function(data) { // Handle response
switch(data.status) {
case: "vbankIssued":
// Logic for a virtual account
break;
case: "success":
// Logic for a successful payment
break;
}
});
} else {
alert("Payment failed. Error: " + rsp.error_msg);
}
});
imp_uid
and merchant_uid
to the specified callback URL, when: the payment by credit card is completed, the customer pay into the issued virtual account, and the payment is refunded in Iamport Admin Dashboard. The merchant server inquires the payment information from the Iamport server with the received imp_uid
and synchronizes the data based on the status.Iamport Webhook
POST
request generated when Iamport Webhook is called. app.use(bodyParser.json());
...
// Handle POST request for "/iamport-webhook"
app.post("/iamport-webhook", async (req, res) => {
try {
const { imp_uid, merchant_uid } = req.body; // Extract imp_uid and merchant_uid from req body
} catch (e) {
res.status(400).send(e);
}
})
POST
request for /iamport-webhook
, and imp_uid
and merchant_uid
were extracted from the HTTP request body.https:\//api.iamport.kr/users/getToken
using the REST API Key
and REST API Secret
. Then, the access token and the extracted imp_uid
are used to search for payment information through a request to https:\//api.iamport.kr/payments/$imp_uid
. app.use(bodyParser.json());
...
// Handle POST request for "/iamport-webhook"
app.post("/iamport-webhook", async (req, res) => {
try {
const { imp_uid, merchant_uid } = req.body; // Extract imp_uid and merchant_uid from req body
// Acquire access token
const getToken = await axios({
url: "https://api.iamport.kr/users/getToken",
method: "post", // POST method
headers: { "Content-Type": "application/json" }, // "Content-Type": "application/json"
data: {
imp_key: "imp_apikey", // REST API key
imp_secret: "ekKoeW8RyKuT0zgaZsUtXXTLQ4AhPFW3ZGseDA6bkA5lamv9OqDMnxyeB9wqOsuO9W3Mx9YSJ4dTqJ3f" // REST API Secret
}
});
const { access_token } = getToken.data.response; // Authentication token
...
// Query payment information from Iamport with imp_uid
const getPaymentData = await axios({
url: \`https://api.iamport.kr/payments/\${imp_uid}\`, // pass imp_uid
method: "get", // GET method
headers: { "Authorization": access_token } // Append the authentication token to Authorization header
});
const paymentData = getPaymentData.data.response; // Query result
...
} catch (e) {
res.status(400).send(e);
}
});
REST API Key
and REST API Secret
issued for Iamport account to retrieve payment information. The access token request was sent to https:\//api.iamport.kr/users/getToken
using POST
method, with application/json
as Content-Type
, imp_key: REST API key, and imp_secret: REST API Secret
in the body.
When the server responses to the request, it stores the access_token
, which is the access token included in the response data. Then, the URL https:\//api.iamport.kr/payments/{imp_uid}
was built with imp_uid
, the issued access token access_token
was added to Authorization
, and finally the payment information was queried by sending a request using GET
method.status
. app.use(bodyParser.json());
...
// Handle POST request for "/iamport-webhook"
app.post("/iamport-webhook", async (req, res) => {
try {
const { imp_uid, merchant_uid } = req.body; // Extract imp_uid and merchant_uid from req body
// Acquire access token
/* ...code omitted here... */
// Query payment information from Iamport with imp_uid
/* ...code omitted here... */
const paymentData = getPaymentData.data.response; // Query result
...
// Query the amount to be paid from the database
const order = await Orders.findById(paymentData.merchant_uid);
const amountToBePaid = order.amount; // Amount to be paid
...
// Verify payment
const { amount, status } = paymentData;
if (amount === amountToBePaid) { // Match the payment amount payment amount === amount to be paid
await Orders.findByIdAndUpdate(merchant_uid, { $set: paymentData }); // Store the payment information in the database
switch (status) {
case "ready": // Issue a virtual account
// Store the virtual account issue information in database
const { vbank_num, vbank_date, vbank_name } = paymentData;
await Users.findByIdAndUpdate("/* Customer id */", { $set: { vbank_num, vbank_date, vbank_name }});
// Send text message for issuance of virtual account
SMS.send({ text: \`Virtual account has been successfully issued. Account information \${vbank_num} \${vbank_date} \${vbank_name}\`});
res.send({ status: "vbankIssued", message: "Virtual account issued successfully" });
break;
case "paid": // Payment complete
res.send({ status: "success", message: "Payment succeeded" });
break;
}
} else { // Payment amount mismatch. Payment is forged
throw { status: "forgery", message: "Forged payment attempted" };
}
} catch (e) {
res.status(400).send(e);
}
});
merchart_uid
to find out the amount that should have been paid.
Then, the actual amount
paid was compared to the amount that should have been paid, amountToBePaid
, to check for forgery in payment. If the payment amounts are matched, the payment information is stored in the database and an appropriate response is sent based on the payment status
.KG Inicis
, LG U+
, NHN KCP
, NICE
, JTNet
, KICC
are used as PG services.
Unlike PC, KG Inicis
, LG U+
, NHN KCP
, NICE
, JTNet
, and KICC
provide a payment process for mobile web environments that redirects to the website of each PG service. Therefore, even if IMP.request_pay(param, callback)
is called, callback
is not executed after payment is completed.Why callback is not executed?
IMP.request_pay
is released from memory, so callback cannot be executed when payment is completed.
url
after the payment is completed. Here, you can specify the url
you want to redirect to the m_redirect_url
attribute of the param
.IMP.request_pay(param, callback)
, specify the url
to be redirected after payment is completed in the m_redirect_url
attribute of param
, which is the first parameter of the function. // Call IMP.request_pay(param, callback)
IMP.request_pay({
/* ...code omitted here... */,
m_redirect_url: "https://www.myservice.com/payments/complete/mobile"
}, /* callback */); // callback is not executed
myservice.com
to your real web server domain.GET
request will be created. At this time imp_uid
, merchant_uid
, imp_success
, error_code
and error_msg
are added to the query string of the url
.What does 'payment completed' exactly mean?
Payment completed
means all the following cases.
paid
, imp_success: true
)failed
, imp_success: false
)ready
, imp_success: true
)m_redirect_url
as https:\//myservice.com/payments/complete
. curl https://myservice.com/payments/complete?imp_uid=iamport_unique_id&merchant_uid=unique_order_number&imp_success=true
URL
, and perform transaction verification and data synchronization with the received imp_uid
and merchant_uid
.imp_success
parameter means that a payment process has ended without any problems. But do not judge the success of the payment by the imp_success
value because the amount could be forged.If imp_success
is true, you have to pass payment results(imp_uid
, merchant_uid
) to your web server(POST
request) and check if the amount is forged. Unless, you need to write your own code to show the reason of failure to customers.success
query parameter is passed, not imp_success
. And for real-time account transfer of KG Inicis, no parameter which is substitute for imp_success
is passed. So keep in mind with that.
app.use(bodyParser.json());
...
// Handles the GET request for "/payments/complete/mobile/"
app.get("/payments/complete/mobile/", async (req, res) => {
try {
const { imp_uid, merchant_uid } = req.query; // Extract imp_uid and merchant_uid from req query
// Acquire access token
const getToken = await axios({
url: "https://api.iamport.kr/users/getToken",
method: "post", // POST method
headers: { "Content-Type": "application/json" }, // "Content-Type": "application/json"
data: {
imp_key: "imp_apikey", // REST API key
imp_secret: "ekKoeW8RyKuT0zgaZsUtXXTLQ4AhPFW3ZGseDA6bkA5lamv9OqDMnxyeB9wqOsuO9W3Mx9YSJ4dTqJ3f" // REST API Secret
}
});
const { access_token } = getToken.data.response; // Authentication token
...
// Query payment information from Iamport with imp_uid
const getPaymentData = await axios({
url: \`https://api.iamport.kr/payments/\${imp_uid}\`, // pass imp_uid
method: "get", // GET method
headers: { "Authorization": access_token } // Append the authentication token to Authorization header
});
const paymentData = getPaymentData.data.response; // Query result
...
// Query the amount to be paid from the database
const order = await Orders.findById(paymentData.merchant_uid);
const amountToBePaid = order.amount; // Amount to be paid
...
// Verify payment
const { amount, status } = paymentData;
if (amount === amountToBePaid) { // Match the payment amount payment amount === amount to be paid
await Orders.findByIdAndUpdate(merchant_uid, { $set: paymentData }); // Store the payment information in the database
switch (status) {
case "ready": // Issue a virtual account
// Store the virtual account issue information in database
const { vbank_num, vbank_date, vbank_name } = paymentData;
await Users.findByIdAndUpdate("/* Customer id */", { $set: { vbank_num, vbank_date, vbank_name }});
// Send text message for issuance of virtual account
SMS.send({ text: \`Virtual account has been successfully issued. Account information \${vbank_num} \${vbank_date} \${vbank_name}\`});
res.send({ status: "vbankIssued", message: "Virtual account issued successfully" });
break;
case "paid": // Payment complete
res.send({ status: "success", message: "Payment succeeded" });
break;
}
} else { // Payment amount mismatch. Payment is forged
throw { status: "forgery", message: "Forged payment attempted" };
}
} catch (e) {
res.status(400).send(e);
}
});
GET
request for /payments/complete/mobile
. imp_uid
and merchant_uid
were extracted from the query string in the HTTP request URL. After acquiring the transaction information through the Iamport REST API using the extracted data, logic such as transaction verification and storing in the database were performed to generate an appropriate response.