Shoppingate adds its own APIs on top of the standard Mini App ones — orders, payment, refunds, support tickets and the app language. These extended APIs must be declared in SGConf.js before ft.<name>(...) will work.
There are two kinds of APIs in a Mini App. Only the Shoppingate extended APIs need to be registered.
getUserInfo, getSetting, authorize, showModal, navigateTo, getStorageSync, …orderPayment, getUserOrders, createTicket, …getUserInfo is not in SGConf.js and does not need to be — it ships with the runtime. See Permissions for how to call it.
Register only the APIs you actually call.
Every API your Mini App can use is listed in the Shoppingate developer portal. Public APIs are available to every Mini App. Private APIs must be requested first and approved by an admin before your Mini App can call them. The full walkthrough is on the Dev portal → App Binding page.
In the developer portal, open:
Mini-Program → your Mini App → Information Details → App Binding
→ API Configuration
API Configuration shows two lists:
Each row has the same columns:
ft.<name>user api, order apis, tickets apisPublic or Private
Click Details on the API in the Restricted API list. In API use application, write why your Mini App needs it, then click Application use.

Once an admin approves the request, the API moves to the Public API list. Its Usable Range still says Private — that is expected. It is now available to your Mini App, and you can register it in SGConf.js.

The portal gives you the name and a one-line description only. The parameters and the response for each API are in the API reference below. The extended APIs at the time of writing:
'en' or 'ar'. The one extended API that takes no miniappId.id to createTicket.Create one file at the project root, exporting an extApi array — one entry per API, with its name, whether it is synchronous, and its parameters. This example registers every API in the list above:
// SGConf.js
module.exports = {
extApi: [
// user apis
{
name: 'getAppLang',
sync: false,
params: {}
},
// order apis
{
name: 'orderPayment',
sync: false,
params: {
orderInfo: {},
idempotencyKey: '',
miniappId: '',
}
},
{
name: 'getUserOrder',
sync: false,
params: {
orderId: '',
miniappId: '',
}
},
{
name: 'getUserOrders',
sync: false,
params: {
page: 1,
perPage: 5,
miniappId: '',
},
},
{
name: 'refundOrder',
sync: false,
params: {
orderId: '',
amount: 0.0,
reason: '',
miniappId: '',
}
},
{
name: 'getOrderRefunds',
sync: false,
params: {
orderId: '',
miniappId: '',
}
},
// ticket apis
{
name: 'getTicketIssues',
sync: false,
params: {
miniappId: '',
}
},
{
name: 'createTicket',
sync: false,
params: {
issueId: 0,
orderId: '',
reason: '',
affectedProducts: [],
imageFilePath: '',
miniappId: '',
}
},
{
name: 'getUserTickets',
sync: false,
params: {
orderId: '',
miniappId: '',
page: 1,
limit: 10,
}
},
]
}Three rules for an entry:
name must match the portal exactly. It is also the method name on ft.sync: false for all of these — they take success / fail callbacks.params declares the parameter names with placeholder values of the right type ('' string, 0 / 1 / 0.0 number, {} object, [] array). The values are not defaults; only the shape matters.miniappId is required on every extended API except getAppLang. Read it once at launch and keep it in globalData.// app.js
App({
globalData: {
miniappId: '',
},
onLaunch() {
const info = ft.getAccountInfoSync();
this.globalData.miniappId = info && info.miniProgram && info.miniProgram.appId;
},
})Nothing in your code imports SGConf.js. There is no require('./SGConf.js') anywhere, and the ft.* calls still work. The toolchain reads the file by name from the project root and registers the methods on the ft global.
So: put the file at the root, use the exact filename, and do not import it.
Every extended API is a method on ft, with success and fail callbacks — the same shape as the built-in APIs. The parameters are the ones you declared in params.
// pages/bookings/bookings.js
const app = getApp()
ft.getUserOrders({
page: 1,
perPage: 10,
miniappId: app.globalData.miniappId,
success: (res) => {
console.log(res.result, res.total, res.orders);
},
fail: (err) => console.log('getUserOrders fail', err),
});import { useDidShow } from '@tarojs/taro'
useDidShow(() => {
ft.getUserOrders({
page: 1,
perPage: 10,
miniappId,
success: (res) => setOrders(res.orders),
fail: (err) => console.log('getUserOrders fail', err),
})
})const app = createApp({
onShow() {
ft.getUserOrders({
page: 1,
perPage: 10,
miniappId,
success: (res) => { store.orders = res.orders },
fail: (err) => console.log('getUserOrders fail', err),
})
},
})ft, never through Taro.*. Taro wraps the standard cross-platform APIs; it has no wrapper for a host's custom APIs, so there is nothing for Taro.getUserOrders to be. This is true in every project type.Extended APIs answer with a result field, not the errMsg string the built-in APIs use:
{ result: 'success', /* ...payload... */ }
{ result: 'failure', message: 'why it failed' }Check result for the outcome and read message when it failed. The payload key differs per API — orders, order, refunds, tickets, issues, refundInfo — listed in the reference below.
Every API with its parameters, an example call and what it returns. The examples read miniappId from app.globalData, as set up in Step 2.
Returns the host app's current language — 'en' or 'ar'. The one extended API that takes no miniappId.
None.
ft.getAppLang({
success: (res) => {
if (res.result === 'success') {
console.log(res.language); // 'en' or 'ar'
}
},
fail: (err) => console.log('getAppLang fail', err),
});{ result: 'success', language: 'en' } // or 'ar'
{ result: 'failure', language: null }Creates the order and opens the payment sheet. See the full page: orderPayment.
vendor_order_ref. All fields are on the orderPayment page.ft.getAccountInfoSync(); in a web view, ft.getAppletInfo.ft.orderPayment({
orderInfo: {
vendor_order_ref: '1234567orderid',
items: [ /* your items */ ],
subtotal: '98.00',
total_amount: '98.00',
currency: 'SAR',
},
idempotencyKey: 'your-idempotency-key',
miniappId: app.globalData.miniappId,
success: (res) => {
if (res.result !== 'success') {
console.log('orderPayment failed', res.message);
return;
}
console.log(res.status, res.order_id);
},
fail: (err) => console.log('orderPayment fail', err),
});// failure
{ result: 'failure', message }
// success
{ result: 'success', status, order_id, message }status is the outcome of the payment attempt, not an order status:
'already_paid' — paid in an earlier session, nothing left to charge'created' — the order exists, the user backed out without paying⚠️ order_id here is the super app's uuid, not your reference. This is the one API that returns the uuid. Your own reference goes out in orderInfo.vendor_order_ref, and that is what deep links come back on.
Returns one order of the current user.
ft.getAccountInfoSync(); in a web view, ft.getAppletInfo.ft.getUserOrder({
orderId: '1234567orderid',
miniappId: app.globalData.miniappId,
success: (res) => {
if (res.result === 'success') {
console.log(res.order);
}
},
fail: (err) => console.log('getUserOrder fail', err),
});{ result, order: { /* order */ } }Returns the current user's orders in this Mini App, one page at a time.
1.ft.getAccountInfoSync(); in a web view, ft.getAppletInfo.ft.getUserOrders({
page: 1,
perPage: 10,
miniappId: app.globalData.miniappId,
success: (res) => {
if (res.result === 'success') {
console.log(res.total, res.orders);
}
},
fail: (err) => console.log('getUserOrders fail', err),
});{ result, total, page, per_page, orders: [ /* order */ ] }orders is an order object.Requests a refund for an order — the full amount or part of it.
ft.getAccountInfoSync(); in a web view, ft.getAppletInfo.ft.refundOrder({
orderId: '1234567orderid',
amount: 25.0,
reason: 'Item not available',
miniappId: app.globalData.miniappId,
success: (res) => {
if (res.result === 'success') {
console.log(res.refundInfo);
}
},
fail: (err) => console.log('refundOrder fail', err),
});{ result, refundInfo: { /* refund */ } }Returns the refunds of one order.
ft.getAccountInfoSync(); in a web view, ft.getAppletInfo.ft.getOrderRefunds({
orderId: '1234567orderid',
miniappId: app.globalData.miniappId,
success: (res) => {
if (res.result === 'success') {
console.log(res.refunds);
}
},
fail: (err) => console.log('getOrderRefunds fail', err),
});{ result, refunds: [ /* refund */ ] }refunds is a refund object.Returns the list of issue types a user can pick when opening a ticket. Pass the chosen id to createTicket.
ft.getAccountInfoSync(); in a web view, ft.getAppletInfo.ft.getTicketIssues({
miniappId: app.globalData.miniappId,
success: (res) => {
if (res.result === 'success') {
console.log(res.issues); // [ { id, name, code } ]
}
},
fail: (err) => console.log('getTicketIssues fail', err),
});{ result, issues: [ { id, name, code } ] }code is currently null on every issue. Use id.Opens a support ticket for an order on behalf of the user.
id of an issue from getTicketIssues.ft.chooseImage (res.tempFilePaths[0]), or in a native page ft.chooseMedia (res.tempFiles[0].tempFilePath). See Permissions → pickers.ft.getAccountInfoSync(); in a web view, ft.getAppletInfo.ft.createTicket({
issueId: 3,
orderId: '1234567orderid',
reason: 'The order arrived damaged',
affectedProducts: [],
imageFilePath: imagePath, // from ft.chooseMedia
miniappId: app.globalData.miniappId,
success: (res) => {
if (res.result !== 'success') {
console.log('createTicket failed', res.message);
return;
}
console.log(res.ticket);
},
fail: (err) => console.log('createTicket fail', err),
});{ result, message, ticket: { /* ticket */ } }Returns the current user's tickets for an order, one page at a time.
ft.getAccountInfoSync(); in a web view, ft.getAppletInfo.1.10.ft.getUserTickets({
orderId: '1234567orderid',
miniappId: app.globalData.miniappId,
page: 1,
limit: 10,
success: (res) => {
if (res.result === 'success') {
console.log(res.total, res.tickets);
}
},
fail: (err) => console.log('getUserTickets fail', err),
});{ result, total, page, limit, tickets: [ /* ticket */ ] }tickets is a ticket object.Returned by getUserOrder (as order) and getUserOrders (in orders).
1234567orderid — not the super app's uuid.'created' means unpaid.orderInfo.items when you created the order with orderPayment — so the shape is yours. For example name, qty, unit_price, sku, booking_date; there can be more fields or fewer.SAR.getUserOrder; a plain string in getUserOrders.Returned by refundOrder (as refundInfo) and getOrderRefunds (in refunds).
{ id, name }.Returned by createTicket (as ticket) and getUserTickets (in tickets).
{ id, name }. Open statuses are open, pending and in_progress.{ name }.getUserTickets.