Subscription Payments


This guide describes how to integrate subscription payments into your website.After registering a card, you can get a billing key and implement your subscription payments as follows:
  • Immediate subscription payment: request immediate payment > schedule payment/implement recurring payment
  • Delayed subscription payment: schedule payment/implement recurring payment
  • Pay-As-You-Go: request payment (after each use or at a desired time)
STEP1Register card and get billing key
Send card information to the card company in exchange for a billing key.

What is a billing key?

A billing key is an encryption key that can be used to make a payment at a desired time, such as subscriptions and Pay-As-You-Go payments. Since a merchant cannot store the customer's card information, it can get the card's billing key from the card company, store and use it to make one-time or recurring payments.

customer_uid

The billing key is issued by the card company and is stored on the i'mport server using the specified customer_uid as the unique key. For security reasons, the server cannot access the billing key directly, so you must use the customer_uid that corresponds to the billing key.

If the customer registers multiple cards, a unique customer_uid must be created for each card.

Example: If the customer's unique key = gildong_0001 and the last four digits of the card = 1234: customer_uid = gildong_0001_1234
Depending on the PG, the billing key can be issued using the following two methods:
  • A. REST API: NICE Payments, JTNet, KCP, KG INICIS, DaouData(PAY JOA)
  • B. Payment window: JTNet, KG INICIS, Danal-Credit Card, Danal-Mobile, KG Mobilians, Payco, KICC, KCP, Kakao Pay, Naver Pay

A. REST API

NICE Payments, JTNet, KCP, KG INICIS, DaouData(PAY JOA)

The server calls i'mport REST API to send card information to the i'mport server, and the i'mport server calls PG's API to receive a billing key. The card information is not saved during this process.

This method has the following pros/cons:
  • Pros: can customize card registration form.
  • Cons: must establish security process for transmitting card information and specify the Terms of Use and Privacy Policy.

Using API to request billing key to each PG

For PG specific information on using API to request billing key, refer to each API guide from the Subscription payment (billing) integration by PG page.
1Create card registration form
client-side

Add input fields for card information. Create a hidden field to store the customer_uid to send to the server upon form submission. For corporate cards (excluding cards issued under an employee's name), enter a 10-digit business registration number for the birth property.

Call a POST request for /subscription/issue-billing with input values and customer_uid when the Pay button is clicked as follows:
  <form action="{URL to receive billing key request}", method="post">
  <!--Example: https://www.myservice.com/subscription/issue-billing-->
    <div>
        <label for="card_number">Card number XXXX-XXXX-XXXX-XXXX</label>
        <input id="card_number" type="text" name="card_number">
    </div>
    <div>
        <label for="expiry">Card expiration YYYY-MM</label>
        <input id="expiry" type="text" name="expiry">
    </div>
    <div>
        <label for="birth">Date of birth YYMMDD</label>
        <input id="birth" type="text" name="birth">
    </div>
    <div>
        <label for="pwd_2digit">First 2-digits of card password XX</label>
        <input id="pwd_2digit" type="text" name="pwd_2digit">
    </div>
    <input hidden type="text" value="gildong_0001_1234" name="customer_uid">
    <input type="submit" value="Pay">
  </form>
2Get card information
server-side

Create an API endpoint to retrieve the card information from the request body.

You can do this by creating an API endpoint to process a POST request to /subscription/issue-billing as follows:
  // Route POST request to "/subscription/issue-billing"
  app.post("/subscriptions/issue-billing", async (req, res) => {
    try {
      const {
        card_number, // Card number
        expiry, // Card expiration
        birth, // Date of birth
        pwd_2digit, // First 2-digits of card password
        customer_uid, // Unique ID for each card (billing key)
      } = req.body; // Get card info from req.body
      ...
    } catch (e) {
      res.status(400).send(e);
    }
  });
3Request billing key and handle response
server-side

To get the billing key, you must first get a REST API access token.

Use the access token and customer_uid to call the REST API (POST https://api.iamport.kr/subscribe/customers/${customer_uid}) to request for a billing key and return a response based on the result code as follows:
    // Route POST request to "/subscription/issue-billing"
    app.post("/subscriptions/issue-billing", async (req, res) => {
      try {
        const {
          card_number, // Card number
          expiry, // Card expiration
          birth, // Date of birth
          pwd_2digit, // First 2-digits of card password
          customer_uid, // Unique ID for each card (billing key)
        } = req.body; // Get card info from req.body
        ...
        // Get billing key
        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 access token
        const issueBilling = await axios({
          url: \`https://api.iamport.kr/subscribe/customers/\${customer_uid}\`,
          method: "post",
          headers: { "Authorization": access_token }, // Add access token to authorization header
          data: {
            card_number, // Card number
            expiry, // Card expiration
            birth, // Date of birth
            pwd_2digit, // First 2-digits of card password
          }
        });
        ...
        const { code, message } = issueBilling.data;
        if (code === 0) { // Billing key request successful
          res.send({ status: "success", message: "Billing has successfully issued" });
        } else { // Billing key request failed
          res.send({ status: "failed", message });
        }
      } catch (e) {
        res.status(400).send(e);
      }
    });

Request billing key and make payment at once

By using the i'mport REST API /subscribe/payments/onetime, you can get the billing key and make a payment in a single API call.

B. Payment window

JTNet, KG INICIS, Danal-Credit Card, Danal-Mobile, KG Mobilians, Payco, KICC, KCP, Kakao Pay, Naver Pay

You can enter the card information through the PG's payment window to receive a billing key.

This method has the following pros/cons:
  • Pros: no need to establish additional security process for transmitting card information because the data is transmitted directly to PG without going through the server or i'mport server.
  • Cons: cannot customize card registration form since billing key is requested through the PG's payment window.

Using payment window to request billing key to each PG

Since each PG requires different param settings when opening the payment window, refer to each payment window guide from the Subscription payment (billing) integration by PG page.
1Request billing key
client-side

As with requesting for payment, first add the i'mport library and prepare to open the payment window. To request for a billing key, call IMP.request_pay with the required card information. In addition, you must set the following property in IMP.request_pay.param to to get a billing key.

Mobile web environment

In the mobile web, the page is redirected to each PG's website for the following: KG INICIS, NHN KCP, JTNet, and KICC. Refer to Mobile web payments (applicable to select PGs) of the General Payments guide to make a billing key request.
  // Request billing key
  IMP.request_pay({ // Refer to above mentioned PG specific guide for param settings
    customer_uid: "gildong_0001_1234", // Unique ID for each card (billing key)
    /* ...Omitted... */
  }, function (rsp) { // callback
    if (rsp.success) {
      // Billing key request successful
    } else {
      // Billing key request failed
    }
  });
2Handle response
client-side server-side

After successfully getting the billing key, the client sends the customer_uid to the server as follows:
  // Request billing key
  IMP.request_pay({ // param
    /* ...Omitted... */
  }, function (rsp) { // callback
    if (rsp.success) {
      // Billing key request successful
      // jQuery HTTP request
      jQuery.ajax({
        url: "{URL to receive customer-uid}", // Example: https://www.myservice.com/billings/
        method: "POST",
        headers: { "Content-Type": "application/json" },
        data: {
          customer_uid: "gildong_0001_1234", // Unique ID for each card (billing key)
        }
      });
    } else {
      // Billing key request failed
    }
  });
The server creates an API endpoint that receives the customer_uid from the client. The server can use the customer_uid to request payment in the future.
  // Route POST request to "/billings"
  app.post("/billings", async (req, res) => {
    try {
      const { customer_uid } = req.body; // Get customer_uid from req.body
        ...
    } catch (e) {
      res.status(400).send(e);
    }
  });
STEP2Request payment
server-side

To make a payment request, call the recurring payment request API with the billing key obtained through card registration.

First, get the customer_uid for the card from the database, and then get a REST API access token.

Use the access token and customer_uid to call the REST API (POST https://api.iamport.kr/subscribe/payments/again) to request payment as follows:
  // Route POST request to "/billings"
  app.post("/billings", async (req, res) => {
    try {
      const { customer_uid } = req.body; // Get customer_uid from req.body
      // Get billing key
      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
      ...
      // Request payment (for first or nth time)
      const paymentResult = await axios({
        url: \`https://api.iamport.kr/subscribe/payments/again\`,
        method: "post",
        headers: { "Authorization": access_token }, // Add access token to authorization header
        data: {
          customer_uid,
          merchant_uid: "order_monthly_0001", // Order ID
          amount: 8900,
          name: "recurring payments for monthly subscription"
        }
      });
      ...
      const { code, message } = paymentResult;
      if (code === 0) { // Successful communication with the card company (additional check is required to confirm whether the payment was successful)
        if ( paymentResult.status === "paid" ) { // Payment approved
          res.send({ ... });
        } else { // Payment not approved (Example: card limit exceeded, card suspended, insufficient balance)
          // paymentResult.status: failed is returned
          res.send({ ... });
        }
        res.send({ ... });
      } else { // Card company rejected payment (paymentResult is null)
        res.send({ ... });
      }
    } catch (e) {
      res.status(400).send(e);
    }
  });
STEP3Schedule payment
server-side

For subscription products, you can schedule a payment at a desired time in the future.
1Schedule payment
Call the REST API (POST https://api.iamport.kr/subscribe/payments/schedule) to schedule a payment. You can set multiple schedules for a single customer_uid in the API call.
  // Schedule payment
  axios({
    url: \`https://api.iamport.kr/subscribe/payments/schedule\`,
    method: "post",
    headers: { "Authorization": access_token }, // Add access token to authorization header
    data: {
      customer_uid: "gildong_0001_1234", // Unique ID for each card (billing key)
      schedules: [
        {
          merchant_uid: "order_monthly_0001", // Order ID
          schedule_at: 1519862400, // Schedule time in UNIX timestamp.
          amount: 8900,
          name: "Recurring payment for monthly subscription",
          buyer_name: "Hong Gildong",
          buyer_tel: "01012345678",
          buyer_email: "gildong@gmail.com"
        }
      ]
    }
  });
2Save payment result
server-side

When payment is attempted at the scheduled time, a webhook event occurs and the payment ID (imp_uid) and order ID (merchant_uid) are sent to the specified callback URL on the server.

i'mport Webhook

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.
    // Route POST request to "/iamport-callback/schedule"
    app.post("/iamport-callback/schedule", async (req, res) => {
      try {
        const { imp_uid, merchant_uid } = 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
        const { status } = paymentData;
        if (status === "paid") { // If payment is successful
          // Save payment info in DB
          await Orders.findByIdAndUpdate(merchant_uid, { $set: paymentData }); // Mongoose
          ...
        } else {
          // Retry payment
        }
      } catch (e) {
        res.status(400).send(e);
      }
    });
STEP4Recurring payment
server-side

To implement recurring payments, recursively schedule a payment from the server to the i'mport server. When a payment is attempted at the scheduled time, a notification is sent to the server through the callback URL. When the callback URL is called, the server schedules the next payment during post-payment processing.Process the webhook event that occurs when a scheduled payment is attempted. If the scheduled payment is completed successfully, save the payment information in the database and schedule the next payment as follows:
    // Route POST request to "/iamport-callback/schedule"
    app.post("/iamport-callback/schedule", async (req, res) => {
      try {
        const { imp_uid, merchant_uid } = 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
        const { status } = paymentData;
        if (status === "paid") { // If payment is successful
          // Save payment info in DB
          await Orders.findByIdAndUpdate(merchant_uid, { $set: paymentData }); // Mongoose
          ...
          // Schedule next payment
          axios({
            url: "{URL to receive payment schedule}", // Example: https://api.iamport.kr/subscribe/payments/schedule
            method: "post",
            headers: { "Authorization": access_token }, // Add access token to authorization header
            data: {
              customer_uid: "gildong_0001_1234", // Unique ID for each card (billing key)
              schedules: [
                {
                  merchant_uid: "order_monthly_0001", // Order ID
                  schedule_at: 1519516800, // Schedule time in UNIX timestamp.
                  amount: 8900,
                  name: "Recurring payment for monthly subscription",
                  ...
                }
              ]
            }
          });
        } else {
          // Retry payment
        }
      } catch (e) {
        res.status(400).send(e);
      }
    });