Skip to content

Product Examples ​

Each example below shows the complete integration flow for a product type:

  1. Catalog — product fields from GET /v2/catalog
  2. Options — items from GET /v2/options (for select fields)
  3. Payment — resulting POST /v2/topup request via fulfillment mapping

In each pricing option, code is the stable plan or package identity, denomination is the face value or quantity sent as the topup amount, price.amount is the customer-facing selling price, and cost.amount is the reseller's wholesale cost.

Digi Prepaid — Fixed Amounts ​

Flow: Phone → Select amount → Pay

Catalog Entry ​

json
{
  "code": "D",
  "name": "Digi Prepaid",
  "processing_time": "instant",
  "denomination_currency": "MYR",
  "denomination_unit_price": 1.0,
  "fields": [
    {
      "id": "phone",
      "type": "text",
      "input_mode": "tel",
      "label": "Phone Number",
      "placeholder": "e.g. 0123456789",
      "required": true,
      "role": "account",
      "validation": {
        "pattern": "^01[0-9]{8,9}$",
        "message": "Enter valid Malaysian phone number"
      }
    },
    {
      "id": "amount",
      "type": "select",
      "label": "Select Amount",
      "required": true,
      "role": "pricing",
      "data_source": {
        "type": "reference",
        "endpoint": "/options",
        "params": {
          "product_code": { "static": "D" },
          "field_id": { "static": "amount" }
        }
      }
    }
  ],
  "fulfillment": {
    "account": { "from_field": "phone" },
    "amount": { "from_field": "amount", "path": "denomination" }
  },
  "pricing": {
    "unit_price": "0.9850",
    "currency": "MYR",
    "discount": { "type": "percentage", "value": "1.5%" },
    "price_adjustment": null,
    "has_loss_risk": false
  }
}

Options Response ​

GET /v2/options?product_code=D&field_id=amount

json
{
  "items": [
    {
      "code": "5",
      "label": "RM 5",
      "price": { "amount": "5.00", "currency": "MYR" },
      "cost": { "amount": "4.8750", "currency": "MYR" },
      "denomination": 5
    },
    {
      "code": "10",
      "label": "RM 10",
      "price": { "amount": "10.00", "currency": "MYR" },
      "cost": { "amount": "9.7500", "currency": "MYR" },
      "denomination": 10
    },
    {
      "code": "30",
      "label": "RM 30",
      "price": { "amount": "30.00", "currency": "MYR" },
      "cost": { "amount": "29.2500", "currency": "MYR" },
      "denomination": 30
    },
    {
      "code": "50",
      "label": "RM 50",
      "price": { "amount": "50.00", "currency": "MYR" },
      "cost": { "amount": "48.7500", "currency": "MYR" },
      "denomination": 50
    },
    {
      "code": "100",
      "label": "RM 100",
      "price": { "amount": "100.00", "currency": "MYR" },
      "cost": { "amount": "97.5000", "currency": "MYR" },
      "denomination": 100
    }
  ]
}

data_source.type is reference — these are static denominations. Cache locally.

Payment Request ​

User enters phone 0123456789 and selects RM 30. Fulfillment maps: account ← phone, amount ← amount.denomination.

json
{
  "refid": "your-unique-refid",
  "product": "D",
  "account": "0123456789",
  "amount": "30.00"
}

Pricing: RM 30 × 0.9850 = cost RM 29.55 → margin RM 0.45

Flow: Phone → Fetch plans for that number → Select plan → Pay

Products that support plan extras include extras.subproduct_code in fulfillment, mapped from the selected option's code. A product without extras support does not gain this mapping.

Catalog Entry ​

json
{
  "code": "HI",
  "name": "Hotlink Internet",
  "processing_time": "instant",
  "denomination_currency": "MYR",
  "denomination_unit_price": 1.0,
  "fields": [
    {
      "id": "phone",
      "type": "text",
      "input_mode": "tel",
      "label": "Phone Number",
      "placeholder": "e.g. 0123456789",
      "required": true,
      "role": "account",
      "validation": {
        "pattern": "^01[0-9]{8,9}$",
        "message": "Enter valid Malaysian phone number"
      }
    },
    {
      "id": "plan",
      "type": "select",
      "label": "Select Plan",
      "required": true,
      "role": "pricing",
      "data_source": {
        "type": "dynamic",
        "depends_on": ["phone"],
        "endpoint": "/options",
        "params": {
          "product_code": { "static": "HI" },
          "field_id": { "static": "plan" },
          "account_number": { "from_field": "phone" }
        }
      }
    }
  ],
  "fulfillment": {
    "account": { "from_field": "phone" },
    "amount": { "from_field": "plan", "path": "denomination" },
    "extras": {
      "subproduct_code": { "from_field": "plan", "path": "code" }
    }
  },
  "pricing": {
    "unit_price": "0.9800",
    "currency": "MYR",
    "discount": { "type": "percentage", "value": "2%" },
    "price_adjustment": { "type": "fixed", "value": 1.0, "currency": "MYR" },
    "has_loss_risk": false
  }
}

Options Response ​

GET /v2/options?product_code=HI&field_id=plan&account_number=0178855286

Plans are dynamic — they vary per phone number based on eligibility. Only fetch after the user enters their number (depends_on: ["phone"]).

json
{
  "items": [
    {
      "code": "Everything Unlimited with Unlimited Weekly Pass RM12",
      "label": "Unlimited Weekly Pass",
      "validity": "7 days",
      "description": "Everything Unlimited with Unlimited Weekly Pass RM12",
      "price": { "amount": "12.00", "currency": "MYR" },
      "cost": { "amount": "11.7600", "currency": "MYR" },
      "denomination": 12
    },
    {
      "code": "Unlimited data with hotspot and calls 30-days (3Mbps) H",
      "label": "Unlimited data with hotspot and calls (3Mbps) H",
      "validity": "30 days",
      "price": { "amount": "40.00", "currency": "MYR" },
      "cost": { "amount": "39.2000", "currency": "MYR" },
      "denomination": 40
    },
    {
      "code": "Unlimited data with hotspot and calls 30-days (6Mbps) H",
      "label": "Unlimited data with hotspot and calls (6Mbps) H",
      "validity": "30 days",
      "price": { "amount": "50.00", "currency": "MYR" },
      "cost": { "amount": "49.0000", "currency": "MYR" },
      "denomination": 50
    }
  ]
}

Payment Request ​

User selects "Unlimited data with hotspot and calls (3Mbps) H". Fulfillment maps: account ← phone, amount ← plan.denomination, extras.subproduct_code ← plan.code.

json
{
  "refid": "your-unique-refid",
  "product": "HI",
  "account": "0178855286",
  "amount": "40.00",
  "extras": {
    "subproduct_code": "Unlimited data with hotspot and calls 30-days (3Mbps) H"
  }
}

Always send extras.subproduct_code for mobile internet products. If it is omitted and more than one plan shares the selected denomination, the request is rejected with HTTP 400, statusCode 44 and remarks Multiple plans are available for this amount. Please provide a plan code.; no transaction is created. See Make Payment.

Pricing: RM 40 × 0.9800 = cost RM 39.20 → base margin RM 0.80. With +RM 1.00 fixed adjustment, user pays RM 41.00 → margin RM 1.80.

PTPTN — Account Lookup + Flexible Amount ​

Flow: NRIC → Fetch loan accounts → Select account → Enter amount → Pay

Catalog Entry ​

json
{
  "code": "PTPTN",
  "name": "PTPTN",
  "processing_time": "24_hours",
  "fields": [
    {
      "id": "nric",
      "type": "text",
      "input_mode": "numeric",
      "label": "NRIC Number",
      "placeholder": "Enter 12-digit NRIC",
      "required": true,
      "validation": {
        "pattern": "^[0-9]{12}$",
        "message": "Enter valid 12-digit NRIC"
      }
    },
    {
      "id": "loan_account",
      "type": "select",
      "label": "Select Account",
      "required": true,
      "data_source": {
        "type": "dynamic",
        "depends_on": ["nric"],
        "endpoint": "/options",
        "params": {
          "product_code": { "static": "PTPTN" },
          "field_id": { "static": "loan_account" },
          "account_number": { "from_field": "nric" }
        }
      }
    },
    {
      "id": "amount",
      "type": "money",
      "input_mode": "decimal",
      "label": "Payment Amount",
      "placeholder": "Enter amount",
      "required": true,
      "role": "pricing",
      "currency": "MYR",
      "validation": { "min": 10, "max": 60000 }
    }
  ],
  "fulfillment": {
    "account": { "from_field": "loan_account", "path": "account_number" },
    "amount": { "from_field": "amount" },
    "extras": {
      "ic_number": { "from_field": "nric" },
      "subproduct_code": { "from_field": "loan_account", "path": "code" }
    }
  },
  "pricing": {
    "unit_price": "-0.5000",
    "currency": "MYR",
    "discount": { "type": "fixed", "value": "50 cent" },
    "price_adjustment": { "type": "percentage", "value": 1.01 },
    "has_loss_risk": false
  }
}

Options Response ​

GET /v2/options?product_code=PTPTN&field_id=loan_account&account_number=941123045001

json
{
  "items": [
    {
      "code": "S",
      "label": "KELVIN LEE WEI SERN",
      "description": "SSPN Prime",
      "account_number": "009411230450014"
    }
  ]
}

Note: items include account_number which fulfillment maps to the payment account field (not the NRIC).

Payment Request ​

User enters NRIC 941123045001, selects the SSPN account, enters RM 500. Fulfillment maps: account ← loan_account.account_number, amount ← amount (direct money input), extras.ic_number ← nric, extras.subproduct_code ← loan_account.code.

json
{
  "refid": "your-unique-refid",
  "product": "PTPTN",
  "account": "009411230450014",
  "amount": "500.00",
  "extras": {
    "ic_number": "941123045001",
    "subproduct_code": "S"
  }
}

Pricing: RM 500 - 0.50 = cost RM 499.50 → base margin RM 0.50. With ×1.01 percentage adjustment, user pays RM 505.00 → margin RM 5.50.

JomPAY — Biller Selection ​

Flow: Select biller → Enter references → Enter NRIC → Enter amount → Pay

Catalog Entry ​

json
{
  "code": "JOMPAY",
  "name": "JomPAY",
  "processing_time": "instant",
  "fields": [
    {
      "id": "biller",
      "type": "select",
      "label": "Select Biller",
      "required": true,
      "data_source": {
        "type": "reference",
        "endpoint": "/options",
        "params": {
          "product_code": { "static": "JOMPAY" },
          "field_id": { "static": "biller" }
        }
      }
    },
    {
      "id": "ref1",
      "type": "text",
      "label": "Reference 1",
      "placeholder": "Account/Reference number",
      "required": true,
      "role": "account"
    },
    {
      "id": "ref2",
      "type": "text",
      "label": "Reference 2",
      "placeholder": "Optional reference",
      "required": false
    },
    {
      "id": "nric",
      "type": "text",
      "input_mode": "numeric",
      "label": "NRIC Number",
      "placeholder": "Enter 12-digit NRIC",
      "required": true,
      "validation": {
        "pattern": "^[0-9]{12}$",
        "message": "Enter valid 12-digit NRIC"
      }
    },
    {
      "id": "amount",
      "type": "money",
      "input_mode": "decimal",
      "label": "Payment Amount",
      "required": true,
      "role": "pricing",
      "currency": "MYR",
      "validation": { "min": 1, "max": 30000 }
    }
  ],
  "fulfillment": {
    "account": { "from_field": "ref1" },
    "amount": { "from_field": "amount" },
    "extras": {
      "biller_code": { "from_field": "biller", "path": "code" },
      "ic_number": { "from_field": "nric" },
      "ref2": { "from_field": "ref2", "omit_if_empty": true }
    }
  },
  "pricing": {
    "unit_price": "-0.3000",
    "currency": "MYR",
    "discount": { "type": "fixed", "value": "30 cent" },
    "price_adjustment": { "type": "fixed", "value": 0.5, "currency": "MYR" },
    "has_loss_risk": false
  }
}

Options Response ​

GET /v2/options?product_code=JOMPAY&field_id=biller&limit=3

21,000+ billers — use pagination (limit + page) and cache locally.

json
{
  "items": [
    {
      "code": "818625",
      "label": "TADIKA DIDIKAN SOLEH PLT.",
      "min_amount": { "amount": "1.00", "currency": "MYR" },
      "max_amount": { "amount": "30000.00", "currency": "MYR" }
    },
    {
      "code": "779264",
      "label": "RIM PRO AUTOMOTIVE.",
      "min_amount": { "amount": "1.00", "currency": "MYR" },
      "max_amount": { "amount": "30000.00", "currency": "MYR" }
    }
  ],
  "meta": {
    "current_page": 1,
    "last_page": 7096,
    "per_page": 3,
    "total": 21287
  }
}

Dynamic Amount Limits

Each biller has its own min_amount/max_amount. Use these to validate the payment amount field — they override the static validation.min/max from the catalog, which are fallbacks before a biller is selected.

You can also filter by biller code: GET /v2/options?product_code=JOMPAY&field_id=biller&biller_code=818625

Payment Request ​

User selects biller 818625, enters ref1 ABC123, ref2 is empty (omitted), NRIC 941123045001, amount RM 150.

json
{
  "refid": "your-unique-refid",
  "product": "JOMPAY",
  "account": "ABC123",
  "amount": "150.00",
  "extras": {
    "biller_code": "818625",
    "ic_number": "941123045001"
  }
}

Note: ref2 is omitted because omit_if_empty: true and the value is empty.

Pricing: RM 150 - 0.30 = cost RM 149.70. With +RM 0.50 fixed adjustment, user pays RM 150.50 → margin RM 0.80.

PUBG Mobile — Game Packages ​

Flow: Player ID → Select package → Pay

Catalog Entry ​

json
{
  "code": "PUBG",
  "name": "PUBG Mobile",
  "processing_time": "instant",
  "fields": [
    {
      "id": "player_id",
      "type": "text",
      "input_mode": "numeric",
      "label": "Player ID",
      "placeholder": "Enter your PUBG Player ID",
      "required": true,
      "role": "account",
      "validation": {
        "pattern": "^[0-9]{8,12}$",
        "message": "Enter valid Player ID"
      }
    },
    {
      "id": "package",
      "type": "select",
      "label": "Select Package",
      "required": true,
      "role": "pricing",
      "data_source": {
        "type": "reference",
        "endpoint": "/options",
        "params": {
          "product_code": { "static": "PUBG" },
          "field_id": { "static": "package" }
        }
      }
    }
  ],
  "fulfillment": {
    "account": { "from_field": "player_id" },
    "amount": { "from_field": "package", "path": "denomination" },
    "extras": {
      "subproduct_code": { "from_field": "package", "path": "code" }
    }
  },
  "pricing": {
    "unit_price": "0.9450",
    "currency": "MYR",
    "discount": { "type": "percentage", "value": "5.5%" },
    "price_adjustment": null,
    "has_loss_risk": false
  }
}

Options Response ​

GET /v2/options?product_code=PUBG&field_id=package

This product includes rrp (recommended retail price) in addition to price and cost:

json
{
  "items": [
    {
      "code": "60",
      "label": "60 UC",
      "price": { "amount": "5.00", "currency": "MYR" },
      "cost": { "amount": "4.5000", "currency": "MYR" },
      "rrp": { "amount": "5.50", "currency": "MYR" },
      "denomination": 60
    },
    {
      "code": "325",
      "label": "325 UC",
      "price": { "amount": "23.00", "currency": "MYR" },
      "cost": { "amount": "19.5000", "currency": "MYR" },
      "rrp": { "amount": "25.00", "currency": "MYR" },
      "denomination": 325
    },
    {
      "code": "660",
      "label": "660 UC",
      "price": { "amount": "50.00", "currency": "MYR" },
      "cost": { "amount": "42.0000", "currency": "MYR" },
      "rrp": { "amount": "55.00", "currency": "MYR" },
      "denomination": 660
    }
  ]
}

Payment Request ​

User enters player ID 5123456789 and selects "60 UC". Fulfillment maps: account ← player_id, amount ← package.denomination, extras.subproduct_code ← package.code.

json
{
  "refid": "your-unique-refid",
  "product": "PUBG",
  "account": "5123456789",
  "amount": 60,
  "extras": {
    "subproduct_code": "60"
  }
}

Pricing: Per-item cost is RM 4.50 (from options), price is RM 5.00 → margin RM 0.50.

Pine Labs API Documentation