Accept a payment
This guide shows you how to accept a one-time payment. You'll create an order with payment details, confirm customer intent with an OTP, then wait for payment authorization. The entire flow takes two API calls and about 30 seconds.
An authenticated AI agent can create the order with confirmation, then look it up or render an order card while you guide the customer through OTP and provider authorization.
MCP tools: create_order, get_order or render_order_card
Confirmed MCP actions require explicit form confirmation before Inttegro changes state.
How it works
Every payment follows a three-phase pattern: create the order, confirm customer intent with an OTP, then wait for provider authorization. The entire flow typically completes in under 30 seconds. For detailed information on order and payment status transitions at each step, see the Order lifecycle guide.
Step 1: create the order
Creating an order bundles everything about the transaction—who's paying, what they're buying, and how they'll pay—into a single atomic unit. You'll provide either customer_data for new customers or customer_id for returning ones, along with line_items describing the products, fees, or shipping charges. When you set execute_payment: true, Inttegro begins payment execution and sends a 6-digit OTP to the customer's phone.
Create order
- cURL
- TypeScript
- Go
- Python
- PHP
- Ruby
- Java
- C#
curl https://api.inttegro.com/orders/create \
-H "Authorization: Bearer $INTTEGRO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"request_meta": {
"idempotency_key": "order_2025_001"
},
"execute_payment": true,
"customer_data": {
"name": "Gloria Kesewaa",
"email_address": "[email protected]",
"phone_number": "+233544998605"
},
"payment_method_data": {
"type": "mobile_money",
"mobile_money": {
"network": "mtn",
"account_number": "0544998605"
}
},
"line_items": [
{
"type": "product",
"product": {
"type": "physical",
"name": "Utility Sneakers",
"quantity": 1,
"price": {
"currency": "ghs",
"value": 20000
}
}
}
]
}'
import * as Inttegro from '@inttegro/inttegro-sdk'
const inttegro = new Inttegro.InttegroClient({
apiKey: process.env.INTTEGRO_API_KEY!,
})
const result = await inttegro.orders.create({
requestMeta: {
idempotencyKey: "order_2025_001",
},
executePayment: true,
customerData: {
name: "Gloria Kesewaa",
phoneNumber: "+233544998605",
},
paymentMethodData: {
type: Inttegro.PaymentMethodTypes.MobileMoney,
mobileMoney: {
network: Inttegro.MobileMoneyNetworks.MTN,
accountNumber: "0544998605",
},
},
lineItems: [
{
type: Inttegro.LineItemTypes.Product,
product: {
type: Inttegro.ProductTypes.Physical,
name: "Utility Sneakers",
quantity: 1,
price: {
currency: "ghs",
value: 20000,
},
},
},
],
})
package main
import (
"context"
"log"
"os"
inttegro "github.com/zebodotdev/inttegro-sdk-go/v4"
"github.com/zebodotdev/inttegro-sdk-go/v4/money"
"github.com/zebodotdev/inttegro-sdk-go/v4/paymentmethods"
)
func main() {
ctx := context.Background()
client := inttegro.NewClient(os.Getenv("INTTEGRO_API_KEY"))
params := inttegro.OrderCreateParams{
RequestMeta: &inttegro.RequestMeta{
IdempotencyKey: "order_2025_001",
},
ExecutePayment: inttegro.Bool(true),
CustomerData: &inttegro.CustomerData{
Name: "Gloria Kesewaa",
PhoneNumber: "+233544998605",
},
PaymentMethodData: &inttegro.PaymentMethodData{
Type: inttegro.PaymentMethodTypeMobileMoney,
MobileMoney: &inttegro.MobileMoneyParams{
Network: paymentmethods.MobileMoneyNetworkMTN,
AccountNumber: "0544998605",
},
},
LineItems: []inttegro.OrderLineItemParams{
{
Type: inttegro.LineItemTypeProduct,
Product: &inttegro.ProductLineItemParams{
Type: inttegro.ProductTypePhysical,
Name: "Utility Sneakers",
Quantity: 1,
Price: inttegro.PriceParams{
AmountParams: money.AmountParams{
Currency: money.GHS,
Value: 20000,
},
},
},
},
},
}
result, err := client.Orders.Create(ctx, params)
if err != nil {
log.Fatal(err)
}
_ = result
}
import os
import inttegro
client = inttegro.InttegroClient(api_key=os.environ["INTTEGRO_API_KEY"])
result = client.orders.create(inttegro.orders.CreateRequest(
request_meta=inttegro.orders.RequestMeta(
idempotency_key="order_2025_001",
),
execute_payment=True,
customer_data=inttegro.orders.Customer(
name="Gloria Kesewaa",
phone_number="+233544998605",
),
payment_method_data=inttegro.orders.PaymentMethod(
type=inttegro.PaymentMethodType.MOBILE_MONEY,
mobile_money=inttegro.orders.MobileMoney(
network=inttegro.MobileMoneyNetwork.MTN,
account_number="0544998605",
),
),
line_items=[
inttegro.orders.ProductLineItem(
type=inttegro.LineItemType.PRODUCT,
product=inttegro.orders.Product(
type=inttegro.ProductType.PHYSICAL,
name="Utility Sneakers",
quantity=1,
price=inttegro.orders.PriceParams(
currency=inttegro.Currency.GHS,
value=20000,
),
),
),
],
))
<?php
use Inttegro\Client;
$client = new Client($_ENV['INTTEGRO_API_KEY']);
$result = $client->orders->create([
'request_meta' => [
'idempotency_key' => 'order_2025_001',
],
'execute_payment' => true,
'customer_data' => [
'name' => 'Gloria Kesewaa',
'phone_number' => '+233544998605',
],
'payment_method_data' => [
'type' => \Inttegro\PaymentMethodType::MobileMoney,
'mobile_money' => [
'network' => \Inttegro\MobileMoneyNetwork::MTN,
'account_number' => '0544998605',
],
],
'line_items' => [
[
'type' => \Inttegro\LineItemType::Product,
'product' => [
'type' => \Inttegro\ProductType::Physical,
'name' => 'Utility Sneakers',
'quantity' => 1,
'price' => [
'currency' => 'ghs',
'value' => 20000,
],
],
],
],
]);
require "inttegro"
client = Inttegro::Client.new(api_key: ENV.fetch("INTTEGRO_API_KEY"))
result = client.orders.create(
request_meta: {
idempotency_key: "order_2025_001",
},
execute_payment: true,
customer_data: {
name: "Gloria Kesewaa",
phone_number: "+233544998605",
},
payment_method_data: {
type: Inttegro::PaymentMethodType::MOBILE_MONEY,
mobile_money: {
network: Inttegro::MobileMoneyNetwork::MTN,
account_number: "0544998605",
},
},
line_items: [
{
type: Inttegro::LineItemType::PRODUCT,
product: {
type: Inttegro::ProductType::PHYSICAL,
name: "Utility Sneakers",
quantity: 1,
price: {
currency: "ghs",
value: 20000,
},
},
},
]
)
import com.inttegro.Client;
import com.inttegro.orders.OrderCreateParams;
import com.inttegro.RequestMeta;
import com.inttegro.customers.CustomerData;
import com.inttegro.paymentmethods.PaymentMethodData;
import com.inttegro.paymentmethods.PaymentMethodType;
import com.inttegro.paymentmethods.MobileMoneyParams;
import com.inttegro.paymentmethods.MobileMoneyNetwork;
import com.inttegro.orders.OrderLineItemParams;
import com.inttegro.orders.ProductLineItemParams;
import com.inttegro.products.ProductType;
import com.inttegro.prices.PriceParams;
import com.inttegro.money.Currency;
public class Example {
public static void main(String[] args) throws Exception {
var client = new Client(System.getenv("INTTEGRO_API_KEY"));
var params = OrderCreateParams.builder()
.requestMeta(RequestMeta.builder()
.idempotencyKey("order_2025_001")
.build())
.executePayment(true)
.customerData(CustomerData.builder()
.name("Gloria Kesewaa")
.phoneNumber("+233544998605")
.build())
.paymentMethodData(PaymentMethodData.builder()
.type(PaymentMethodType.MOBILE_MONEY)
.mobileMoney(MobileMoneyParams.builder()
.network(MobileMoneyNetwork.MTN)
.accountNumber("0544998605")
.build())
.build())
.lineItem(OrderLineItemParams.product(ProductLineItemParams.builder()
.type(ProductType.PHYSICAL)
.name("Utility Sneakers")
.quantity(1L)
.price(PriceParams.of(Currency.GHS, 20000))
.build()))
.build();
var result = client.orders().create(params);
}
}
using Inttegro;
using var inttegro = new InttegroClient(
Environment.GetEnvironmentVariable("INTTEGRO_API_KEY")!
);
var result = await inttegro.Orders.CreateAsync(new {
request_meta = new {
idempotency_key = "order_2025_001",
},
execute_payment = true,
customer_data = new {
name = "Gloria Kesewaa",
phone_number = "+233544998605",
},
payment_method_data = new {
type = Inttegro.PaymentMethodType.MobileMoney,
mobile_money = new {
network = Inttegro.MobileMoneyNetwork.MTN,
account_number = "0544998605",
},
},
line_items = new[] {
new {
type = Inttegro.LineItemType.Product,
product = new {
type = Inttegro.ProductType.Physical,
name = "Utility Sneakers",
quantity = 1,
price = new {
currency = "ghs",
value = 20000,
},
},
},
},
});
Response
This is a partial response showing key attributes. See the complete Order object for all available fields.
{
"order": {
"id": "or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt",
"status": "requires_payment",
"customer": {
"id": "cu_abc123",
"name": "Gloria Kesewaa",
},
"payment": {
"id": "py_xyz789",
"status": "requires_action",
"payment_method": {
"id": "pm_saved_method",
"type": "mobile_money",
"network": "mtn"
},
"next_action": {
"type": "confirm_payment",
"confirm_payment": {
"expires_at": "2025-01-13T10:08:00Z",
"request": {
"id": "otc_req_8Ks2Vn",
"recipient": "0544998605",
"sent_via": "sms"
}
}
}
}
}
}
Key attributes:
order.id- Store this for the next steppayment.next_action.type: "confirm_payment"- OTP collection neededconfirm_payment.expires_at- OTP expires in ~8 minutescustomer.id- Save for future orders from this customerpayment_method.id- Save to charge this customer again without re-entering payment details
Step 2: confirm customer intent with OTP
The customer receives a confirmation token. Collect it through your UI and submit it with the order, payment, and confirmation-request IDs from the preceding response. Those four values bind the token to the exact payment action being confirmed.
Confirmation is bound to one order, payment, and confirmation request. Use
order.payment.id as payment_id and
order.payment.next_action.confirm_payment.request.id as confirmation_id,
then submit those values with order_id and token. See Confirm a
payment for complete SDK examples.
Done! Check the order status to confirm payment succeeded:
Check status
- cURL
- TypeScript
- Go
- Python
- PHP
- Ruby
- Java
- C#
curl https://api.inttegro.com/orders/lookup \
-H "Authorization: Bearer $INTTEGRO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"order_id": "or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt"
}'
import * as Inttegro from '@inttegro/inttegro-sdk'
const inttegro = new Inttegro.InttegroClient({
apiKey: process.env.INTTEGRO_API_KEY!,
})
const result = await inttegro.orders.lookup({
orderId: "or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt",
})
package main
import (
"context"
"log"
"os"
inttegro "github.com/zebodotdev/inttegro-sdk-go/v4"
)
func main() {
ctx := context.Background()
client := inttegro.NewClient(os.Getenv("INTTEGRO_API_KEY"))
result, err := client.Orders.Lookup(ctx, "or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt")
if err != nil {
log.Fatal(err)
}
_ = result
}
import os
import inttegro
client = inttegro.InttegroClient(api_key=os.environ["INTTEGRO_API_KEY"])
result = client.orders.lookup("or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt")
<?php
use Inttegro\Client;
$client = new Client($_ENV['INTTEGRO_API_KEY']);
$result = $client->orders->lookup("or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt");
require "inttegro"
client = Inttegro::Client.new(api_key: ENV.fetch("INTTEGRO_API_KEY"))
result = client.orders.lookup(order_id: "or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt")
import com.inttegro.Client;
public class Example {
public static void main(String[] args) throws Exception {
var client = new Client(System.getenv("INTTEGRO_API_KEY"));
var result = client.orders().lookup("or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt");
}
}
using Inttegro;
using var inttegro = new InttegroClient(
Environment.GetEnvironmentVariable("INTTEGRO_API_KEY")!
);
var result = await inttegro.Orders.LookupAsync("or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt");
What happens after payment
When you create an order with customer_data and payment_method_data, Inttegro automatically creates a customer record and attaches the payment method to it. The response includes both customer.id and payment_method.id—store these for future use. Next time this customer checks out, you can skip collecting their details again and charge them instantly. See Charge repeat customers to learn how.
Common patterns
Multiple items
Real shopping carts contain more than just products—there are shipping charges, processing fees, and taxes. The line_items array supports three types: product, shipping, and fee. Each line item has its own structure with a type discriminator and a nested object containing the details. Inttegro automatically sums all the line items to calculate the order total, which you'll see in the line_item_group.total field of the response.
line_items: [
{
type: 'product',
product: {
name: 'Shoes',
quantity: 2,
price: { currency: 'ghs', value: 25000 },
},
},
{
type: 'shipping',
shipping: {
fee: { currency: 'ghs', value: 2000 },
},
},
{
type: 'fee',
fee: {
label: 'Processing Fee',
amount: { currency: 'ghs', value: 500 },
},
},
]
Handling errors
OTP expired or wrong
OTP codes expire after about 8 minutes, and customers sometimes mistype them. When either happens, call /orders/request_confirmation to generate and send a fresh code. The customer can retry with the new code without losing their order or having to start over.
import { InttegroClient } from '@inttegro/inttegro-sdk'
const inttegro = new InttegroClient({
apiKey: process.env.INTTEGRO_API_KEY!,
})
await inttegro.orders.requestConfirmation({
orderId: 'or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt',
})
Customer doesn't have funds
If the customer approves the payment but their mobile money account has insufficient balance, the payment attempt fails after OTP confirmation. The order remains in requires_payment status and its latest payment attempt is marked failed. Keep the API error from the confirmation request for the reason you show in logs or support tooling; the order object does not repeat that error code in payment.latest_attempt. You can prompt the customer to add funds and retry, or offer another payment method.
import { InttegroClient } from '@inttegro/inttegro-sdk'
const inttegro = new InttegroClient({
apiKey: process.env.INTTEGRO_API_KEY!,
})
const order = await inttegro.orders.lookup({
orderId: 'or_48ZW7BGvYUBWc1i6WBkL2jr0iPQP5jUy76mmmHpt',
})
if (order.payment?.latestAttempt?.status === 'failed') {
console.log('The latest payment attempt failed')
}
Testing
Use an application configured for the test environment and only payment details and confirmation values supplied for that environment. When a payment requires confirmation, follow Confirm a payment with order_id, payment_id, confirmation_id, and the supplied token; do not hardcode a universal token value.
Key tips
Phone format: Mobile money providers require phone numbers in E.164 format with country code (e.g., +233544998605). If customers enter local format like 0544998605, prepend the country code before sending to Inttegro—otherwise the OTP won't be delivered.
Store order ID: The moment you receive the order creation response, persist order.id to your database. You'll need this ID for payment confirmation, status lookups, and linking the Inttegro order to your internal records. Don't wait until after OTP confirmation—store it immediately.
Complete example
Here's a full implementation showing order creation, OTP handling, confirmation, and error checking. This example includes database persistence and proper status verification—use it as a starting template for your integration.
import { InttegroClient } from '@inttegro/inttegro-sdk'
const inttegro = new InttegroClient({
apiKey: process.env.INTTEGRO_API_KEY!,
})
async function acceptPayment(
userId: string,
saveOrderId: (userId: string, orderId: string) => Promise<void>,
promptCustomerForOTP: () => Promise<string>,
) {
// 1. Create and charge
const order = await inttegro.orders.create({
requestMeta: {
idempotencyKey: `order_${userId}`,
},
executePayment: true,
customerData: {
name: 'Gloria Kesewaa',
phoneNumber: '+233544998605',
},
paymentMethodData: {
type: 'mobile_money',
mobileMoney: { network: 'mtn', accountNumber: '0544998605' },
},
lineItems: [
{
type: 'product',
product: {
type: 'physical',
name: 'Sneakers',
quantity: 1,
price: { currency: 'ghs', value: 20000 },
},
},
],
})
await saveOrderId(userId, order.id)
// 2. Show an OTP input only when confirmation is required
const payment = order.payment
const nextAction = payment?.nextAction
const confirmation = nextAction?.confirmPayment?.request
if (
payment?.status === 'requires_action' &&
nextAction?.type === 'confirm_payment' &&
payment.id &&
confirmation?.id
) {
const otp = await promptCustomerForOTP()
// 3. Confirm
await inttegro.orders.confirmPayment({
confirmationId: confirmation.id,
orderId: order.id,
paymentId: payment.id,
token: otp,
})
// 4. Read the current state before fulfilling the order
const updated = await inttegro.orders.lookup({ orderId: order.id })
if (updated.payment?.status === 'paid') {
console.log('Payment successful!')
}
}
return order
}
Next steps
-
Charge repeat customers - Fast checkout for returning customers
-
Retry payment with new payment method - Retry failed payments with alternative methods
-
Order now, pay later - Create orders without immediate payment
-
Orders API - Full API reference with all parameters
-
Refunds - Handle returns and cancellations
That's it! You're ready to accept payments.