General Payments


This guide describes how to integrate general payments into your website.
STEP1Add i'mport library
client-side

Add the i'mport library to your checkout page.

For the latest library version information, check the JavaScript SDK Release Notes page.
  <!-- 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-{SDK-latest-version}.js"></script>

You must install jQuery 1.0 or later version.
STEP2Prepare for payment
client-side

On the checkout page, initialize the global object IMP using your Merchant ID.
    var IMP = window.IMP; // Can be omitted
    IMP.init("{Merchant ID}"); // Example: imp00000000
STEP3Request payment
client-side

Call IMP.request_pay by passing the required payment information. This opens the payment page of the specified PG.

Creating order ID (merchant_uid)

Before calling IMP.request_pay, it is recommended to create an order record in the database and pass the order's order ID as param.merchant_uid. After the payment process is complete, the server uses the merchant_uid to retrieve the order information for payment fraud check.
Open the PG's payment page in the onclick event of the Pay button as follows:
  <button onclick="requestPay()">Pay</button>
  ...
  <script>
    function requestPay() {
      // IMP.request_pay(param, callback) -> Open payment page
      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 for when payment is successful
              ...
          } else {
              ...,
              // Logic for when payment fails
              ...
          }
      });
    }
  </script>

STEP4Send payment information
client-side

Based on the the payment result (sucess/fail) in the response object (rsp) returned after the payment process is complete, add the post-payment processing logic in the callback function.

When the payment is successful, add the logic to send the payment ID (imp_uid) and order ID (merchant_uid) to the server as follows:
    IMP.request_pay({
      /* ...Omitted... */
    }, function (rsp) { // callback
      if (rsp.success) { // payment successful: payment accepted or virtual account issued
        // jQuery HTTP request
        jQuery.ajax({
            url: "{server-side endpoint to process the payment information}", // Example: https://www.myservice.com/payments/complete
            method: "POST",
            headers: { "Content-Type": "application/json" },
            data: {
                imp_uid: rsp.imp_uid,
                merchant_uid: rsp.merchant_uid
            }
        }).done(function (data) {
          // Logic for when the server successfully processes the payment information
        })
      } else {
        alert("Payment failed. Error: " + rsp.error_msg);
      }
    });
STEP5Verify and save payment information
server-side

After the server receives the payment information from the client, verify the payment amount for fraud and save the payment information in the database.
1Get payment ID and order ID
Build an API endpoint on the server to get the payment ID (imp_uid) and order ID (merchant_uid).

You can do this by routing a POST request to your service URL (/payments/complete in this example) as follows:
  app.use(bodyParser.json());
  // Route POST request to "/payments/complete"
  app.post("/payments/complete", async (req, res) => {
    try {
      const { imp_uid, merchant_uid } = req.body; // Get imp_uid and merchant_uid from req.body
    } catch (e) {
      res.status(400).send(e);
    }
  });
2Get payment information
To get payment information from the i'mport server, you must first get a REST API access token.

Use the access token and imp_uid (payment ID) to call the REST API (GET https://api.iamport.kr/payments/${imp_uid}) that returns the payment information as follows:
    app.use(bodyParser.json());
    ...
    // Route POST request to "/payments/complete"
    app.post("/payments/complete", async (req, res) => {
      try {
        const { imp_uid, merchant_uid } = req.body; // Get imp_uid and merchant_uid from req.body
        ...
        // Get 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; // Access token
        ...
        // Get payment info from i'mport server using 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 } // Add access token to Authorization header
        });
        const paymentData = getPaymentData.data.response; // Save payment info
        ...
      } catch (e) {
        res.status(400).send(e);
      }
    });
3Verify and save payment information
Compare the original requested amount and the actual processed amount to check for forged or falsified payment amount. If no fraud is detected, save the payment information in the database.

Why fraud detection is necessary

Since the payment request is made on the client side, a payment request can be forged or falsified by manipulating the client script. Therefore, you must compare the origial requested amount with the actual processed amount after the payment process is complete.

For example, when paying for a product that costs 100,000 won, an attacker can manipulate the client script to change the amount property to a value lower than the actual amount. Since you cannot prevent script manipulation on the client, you must check for fraud on the server after the payment is processed.
The original requested amount is queried from the database with merchant_uid, and the actual processed amount is retrieved from the i'mport server with imp_uid. The two values ​​are compared to verfiy that they match. If the verification is successful, the payment information is saved in the database and an appropriate response is returned based on the payment status (status). Otherwise, an error message is returned.
  app.use(bodyParser.json());
  ...
  // Route POST request to "/payments/complete"
  app.post("/payments/complete", async (req, res) => {
    try {
      const { imp_uid, merchant_uid } = req.body; // Get imp_uid and merchant_uid from req.body
      // Get access token
      /* ...Omitted... */
      // Get payment info from i'mport server using imp_uid
      /* ...Omitted... */
      const paymentData = getPaymentData.data.response; // Save payment info
      ...
      // Query for original requested amount from the database
      const order = await Orders.findById(paymentData.merchant_uid);
      const amountToBePaid = order.amount; // Original requested amount
      ...
      // Verify payment amount
      const { amount, status } = paymentData;
      if (amount === amountToBePaid) { // Amounts match. Processed amount === Original requested amount
        await Orders.findByIdAndUpdate(merchant_uid, { $set: paymentData }); // Save payment info in DB
        ...
        switch (status) {
          case "ready": // Issue virtual account
            // Save virtual account info in DB
            const { vbank_num, vbank_date, vbank_name } = paymentData;
            await Users.findByIdAndUpdate("/* customer id */", { $set: { vbank_num, vbank_date, vbank_name }});
            // Send virtual account issuance text message
            SMS.send({ text: \`Virtual account has been 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 successful." });
            break;
        }
      } else { // Amount mismatch. Forged/falsified payment.
        throw { status: "forgery", message: "Forged/falsified payment attempted" };
      }
    } catch (e) {
      res.status(400).send(e);
    }
  });

Saving payment information in the database

Payment information should be saved in the database so that customers can check payment details on the order history page after payment. Payment information is updated using the i'mport API whenever payment information is modified in your i'mport account due to events, such as a full or partial refund. Such events are notified to the server so that the change can be updated in the database.
STEP6Handle response from server
client-side

On the page, add the logic to handle the response from the server as follows:
  IMP.request_pay({
    /* ...Omitted... */
  }, function (rsp) { // callback
    if (rsp.success) { // payment successful: payment accepted or virtual account issued
        // jQuery HTTP request
        jQuery.ajax({
          /* ...Omitted... */
        }).done(function(data) { // handle response
          switch(data.status) {
            case: "vbankIssued":
              // Logic for when virtual account is issued
              break;
            case: "success":
              // Logic for when payment is successful
              break;
          }
        });
    } else {
      alert("Payment failed. Error: " + rsp.error_msg);
    }
  });
STEP7Configure notifications using webhook (recommended)
server-side

We strongly recommend setting up the i'mport Webhook as a backup method for when payment information sent from the i'mport server is not delivered to the server or delayed due to unstable device or network. However, if you support virtual account payments, you must use webhook to set up notifications for virtual account deposits.

First, set the server's callback URL to send a notification to when an event occurs on the i'mport server. For information about the i'mport Webhook and how to set the callback URL, refer to the i'mport Webhook guide.

Build an endpoint to handle the POST request of the webhook event, get the payment information, and then verify and save the information.
  app.use(bodyParser.json());
  ...
  // Route POST request to "/iamport-webhook"
  app.post("/iamport-webhook", async (req, res) => {
    try {
        const { imp_uid, merchant_uid } = req.body; // Get imp_uid and merchant_uid from req.body
        // Get access token
        /* ...Omitted... */
        // Get payment info from i'mport server using imp_uid
        /* ...Omitted... */
        const paymentData = getPaymentData.data.response; // Save payment info
        ...
        // Query for original requested amount from the database
        const order = await Orders.findById(paymentData.merchant_uid);
        const amountToBePaid = order.amount; // Original requested amount
        ...
        // Verify payment amount
        const { amount, status } = paymentData;
        if (amount === amountToBePaid) { // Amounts match. Processed amount === Original requested amount
          await Orders.findByIdAndUpdate(merchant_uid, { $set: paymentData }); // Save payment info in DB
          switch (status) {
            case "ready": // Issue virtual account
              // Save virtual account info in DB
              const { vbank_num, vbank_date, vbank_name } = paymentData;
              await Users.findByIdAndUpdate("/* customer id */", { $set: { vbank_num, vbank_date, vbank_name }});
              // Send virtual account issuance text message
              SMS.send({ text: \`Virtual account has been 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 successful." });
              break;
          }
        } else { // Amount mismatch. Forged/falsified payment.
          throw { status: "forgery", message: "Forged/falsified payment attempted" };
        }
    } catch (e) {
      res.status(400).send(e);
    }
  });
STEP8Mobile web payments (applicable to select PGs)
In the mobile web, the page is redirected to each PG's website for the following: KG INICIS, NHN KCP, JTNet, and KICC. With page redirection, the callback function does not execute as it is in the PC web. Instead, you must set the URL to redirect to in the param.m_redirect_url of the IMP.request_pay function.

Why callback does not execute when page is redirected

When the page is redirected to the PG's website, the callback function does not execute after the payment process is complete because the callback function specified in IMP.request_pay is released from memory.
1Set redirection
client-side

In the param.m_redirect_url parameter of IMP.request_pay, set the redirect URL as follows:
  // Call IMP.request_pay(param, callback)
  IMP.request_pay({
      /* ...Omitted... */,
      m_redirect_url: "{redirect URL}" // Example: https://www.myservice.com/payments/complete/mobile
  }, /* callback */); // callback is not called
With m_redirect_url set to https:\//myservice.com/payments/complete, redirect the page based on the result of the payment request (success or fail) as follows:
  curl https://myservice.com/payments/complete?imp_uid=iamport_unique_id&merchant_uid=unique_order_id&imp_success=true

Completion of payment process

The payment process is complete when:

  1. Payment is successful (status: paid, imp_success: true)
  2. Payment fails (status: failed, imp_success: false)
    • Payment page fails to open due to PG module setting error
    • User terminates the payment process by clicking the X or Cancel button
    • Payment is suspended due to invalid card information, limit exceeded, insufficient balance, etc.

  3. Virtual account is issued (status: ready, imp_success: true)
2Verify and save payment information
server-side

Build an endpoint to process the GET request for the redirect URL, and then add the logic to verify and save the payment information as in the PC web.
    app.use(bodyParser.json());
    ...
    // Route GET request to "/payments/complete/mobile/"
    app.get("/payments/complete/mobile/", async (req, res) => {
      try {
          const { imp_uid, merchant_uid } = req.query; // Get imp_uid and merchant_uid from req.query
          // Get 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; // Access token
          ...
          // Get payment info from i'mport server using 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 } // Add Access token to Authorization header
          });
          const paymentData = getPaymentData.data.response; // Save payment info
          ...
          // Query for original requested amount from the database
          const order = await Orders.findById(paymentData.merchant_uid);
          const amountToBePaid = order.amount; // Original requested amount
          ...
          // Verify payment amount
          const { amount, status } = paymentData;
          if (amount === amountToBePaid) { // Amounts match. Processed amount === Original requested amount
            await Orders.findByIdAndUpdate(merchant_uid, { $set: paymentData }); // Save payment info in DB
            switch (status) {
              case "ready": // Issue virtual account
                // Save virtual account info in DB
                const { vbank_num, vbank_date, vbank_name } = paymentData;
                await Users.findByIdAndUpdate("/* customer id */", { $set: { vbank_num, vbank_date, vbank_name }});
                // Send virtual account issuance text message
                SMS.send({ text: \`Virtual account has been 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 successful." });
                break;
            }
          } else { // Amount mismatch. Forged/falsified payment.
            throw { status: "forgery", message: "Forged/falsified payment attempted" };
          }
        } catch (e) {
          res.status(400).send(e);
        }
    });

Determing payment success with imp_success

The imp_success parameter indicates whether or not the payment process completed successfully. However, since the payment page is opened by calling a JavaScript function from the client-side, the payment amount can be forged by a malicious user. Hence, this value should not be used to determine the success of the payment. Depending on the value of imp_success, determine the payment success as follows:

  • imp_success = true: first send payment information (imp_uid & merchant_uid) to the server to verify the payment amount, and then finalize payment success.
  • Imp_success = false: alert the user that the payment failed.

* Note that some PGs return a success parameter instead of imp_success, or return neither. (Example: KG INICIS - instant bank transfer)