Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .do/app-spec-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ services:
http_port: 3456
health_check:
http_path: /health
# Bare /health used DO defaults (1s timeout, 0s initial delay), which killed
# the instance during cold start (exit 128) and failed the whole deploy.
initial_delay_seconds: 30
period_seconds: 10
timeout_seconds: 5
success_threshold: 1
failure_threshold: 9
instance_size_slug: apps-s-1vcpu-0.5gb
instance_count: 1
envs:
Expand All @@ -58,3 +65,7 @@ services:
scope: RUN_TIME
type: SECRET
value: ""
- key: SANDBOX_KEYCLOAK_CLIENT_SECRET
scope: RUN_TIME
type: SECRET
value: ""
38 changes: 34 additions & 4 deletions .github/workflows/deploy-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,44 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Trigger DigitalOcean App Platform Deployment
id: deploy
run: |
echo "Deploying to staging environment..."

curl -X POST \
resp=$(curl -sS -X POST \
-H "Authorization: Bearer ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}" \
-H "Content-Type: application/json" \
"https://api.digitalocean.com/v2/apps/${{ secrets.DO_APP_ID_DEV }}/deployments" \
--fail-with-body
--fail-with-body)
id=$(echo "$resp" | jq -r '.deployment.id')
echo "Triggered deployment $id"
echo "deployment_id=$id" >> "$GITHUB_OUTPUT"

- name: Deployment Triggered
run: echo "Deployment triggered successfully"
# Wait for the deployment to go live, then refresh the MCP index so search
# reflects the just-published docs (the mcp loads the index from the public
# URL at startup, which during a deploy is still the previous build).
# Best-effort: this step must never fail the workflow.
- name: Wait for deploy, then refresh MCP index
run: |
app="${{ secrets.DO_APP_ID_DEV }}"
id="${{ steps.deploy.outputs.deployment_id }}"
if [ -z "$id" ] || [ "$id" = "null" ]; then
echo "No deployment id — skipping refresh."; exit 0
fi
for i in $(seq 1 60); do
phase=$(curl -sS \
-H "Authorization: Bearer ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}" \
"https://api.digitalocean.com/v2/apps/$app/deployments/$id" \
| jq -r '.deployment.phase')
echo "deployment $id phase=$phase"
case "$phase" in
ACTIVE)
echo "Deployment live — refreshing MCP index..."
curl -sS -X POST https://docs.ottu.dev/mcp/refresh && echo " MCP index refreshed"
exit 0 ;;
ERROR|CANCELED|SUPERSEDED)
echo "Deployment ended as $phase — skipping refresh."; exit 0 ;;
esac
sleep 15
done
echo "Timed out waiting for ACTIVE — skipping refresh."; exit 0
38 changes: 34 additions & 4 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,44 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Trigger DigitalOcean App Platform Deployment
id: deploy
run: |
echo "Deploying to production environment..."

curl -X POST \
resp=$(curl -sS -X POST \
-H "Authorization: Bearer ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}" \
-H "Content-Type: application/json" \
"https://api.digitalocean.com/v2/apps/${{ secrets.DO_APP_ID_PROD }}/deployments" \
--fail-with-body
--fail-with-body)
id=$(echo "$resp" | jq -r '.deployment.id')
echo "Triggered deployment $id"
echo "deployment_id=$id" >> "$GITHUB_OUTPUT"

- name: Deployment Triggered
run: echo "Deployment triggered successfully"
# Wait for the deployment to go live, then refresh the MCP index so search
# reflects the just-published docs (the mcp loads the index from the public
# URL at startup, which during a deploy is still the previous build).
# Best-effort: this step must never fail the workflow.
- name: Wait for deploy, then refresh MCP index
run: |
app="${{ secrets.DO_APP_ID_PROD }}"
id="${{ steps.deploy.outputs.deployment_id }}"
if [ -z "$id" ] || [ "$id" = "null" ]; then
echo "No deployment id — skipping refresh."; exit 0
fi
for i in $(seq 1 60); do
phase=$(curl -sS \
-H "Authorization: Bearer ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}" \
"https://api.digitalocean.com/v2/apps/$app/deployments/$id" \
| jq -r '.deployment.phase')
echo "deployment $id phase=$phase"
case "$phase" in
ACTIVE)
echo "Deployment live — refreshing MCP index..."
curl -sS -X POST https://docs.ottu.com/mcp/refresh && echo " MCP index refreshed"
exit 0 ;;
ERROR|CANCELED|SUPERSEDED)
echo "Deployment ended as $phase — skipping refresh."; exit 0 ;;
esac
sleep 15
done
echo "Timed out waiting for ACTIVE — skipping refresh."; exit 0
28 changes: 14 additions & 14 deletions docs/business/wallet/index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Wallet
sidebar_label: Wallet
title: M-Wallet
sidebar_label: M-Wallet
toc_min_heading_level: 2
toc_max_heading_level: 3
---
Expand All @@ -9,11 +9,11 @@ import StepGuide from "@site/src/components/StepGuide";
import { businessWalletScenariosSteps } from "@site/src/data/wallet-scenarios";
import FAQ, { FAQItem } from "@site/src/components/FAQ";

# Wallet
# M-Wallet

Wallet is a stored balance you can grant to a customer in any currency you accept. Once a customer has wallet credit, they can spend it at checkout — fully or partially — on any future order with your business.
M-Wallet (merchant wallet) is a stored balance you can grant to a customer in any currency you accept. Once a customer has wallet credit, they can spend it at checkout — fully or partially — on any future order with your business.

## Why use Wallet
## Why use M-Wallet {#why-use-wallet}

- **Refund without returning funds to the original card.** Useful when cards expire, or when you want to issue store credit instead of cash back.
- **Issue goodwill credit or promotional balances** without payment-gateway fees.
Expand All @@ -26,7 +26,7 @@ Wallet is a stored balance you can grant to a customer in any currency you accep
3. **Customer returns** for a future order with your business.
4. **They pay with wallet** — fully if the balance covers it, partially with another method otherwise.

## Refund to Wallet
## Refund to M-Wallet {#refund-to-wallet}

Refunding to wallet credits the customer's wallet balance instead of returning funds through the payment gateway. The customer can spend the credit on their next order.

Expand Down Expand Up @@ -69,9 +69,9 @@ Refunding to wallet credits the customer's wallet balance instead of returning f
},
]} />

The next time the customer reaches checkout in your store (same currency), they'll see **Wallet (X.XXX USD)** as a payment method. See [Wallet at Checkout](#wallet-at-checkout) below for the customer-side flow.
The next time the customer reaches checkout in your store (same currency), they'll see **Wallet (X.XXX USD)** as a payment method. See [M-Wallet at Checkout](#wallet-at-checkout) below for the customer-side flow.

## Wallet at Checkout
## M-Wallet at Checkout {#wallet-at-checkout}

This section explains what your customer sees when they have a wallet balance and reach checkout — so you can answer support questions and design messaging on your own site.

Expand Down Expand Up @@ -99,7 +99,7 @@ If a checkout offers several payment gateways, make sure at most **one wallet pe

- Display the customer's wallet balance on your own site or app — call the [Wallet Accounts API](/developers/payments/wallet/#api-reference).
- Refund any future order to the same wallet to top it up.
- View full credit and debit history per customer in [Wallet Reporting](#wallet-reporting) below.
- View full credit and debit history per customer in [M-Wallet Reporting](#wallet-reporting) below.

## How fees are charged

Expand Down Expand Up @@ -127,7 +127,7 @@ When the customer's wallet balance is **equal to or greater than the order amoun
In the partial-wallet case above, the customer pays 30.000 at checkout, but only 20.000 of that is principal — the other 10.000 is the fee. The wallet still contributed its full 30.000 toward principal. The fee did not reduce the wallet's contribution; it was added on top of the PG leg.
:::

## Wallet Reporting
## M-Wallet Reporting {#wallet-reporting}

The dashboard provides three screens for tracking wallet activity: **Accounts**, **Ledger**, and **Operations**.

Expand Down Expand Up @@ -196,7 +196,7 @@ Lists individual wallet operations (a single credit, debit, or reservation cycle

## Reconciliation

Wallet-enabled payments settle across two separate rails — the **wallet rail** (funds moved internally between Ottu wallet balances, no card scheme involved) and the **PG rail** (funds charged through a payment gateway and settled by the acquirer). A single customer order may use one rail or both, so your month-end reconciliation needs to account for both sources of funds.
Wallet-enabled payments settle across two separate rails — the **wallet rail** (funds moved internally between wallet balances, no card scheme involved) and the **PG rail** (funds charged through a payment gateway and settled by the acquirer). A single customer order may use one rail or both, so your month-end reconciliation needs to account for both sources of funds.

This section explains how Ottu represents a wallet-touched payment in your transaction records, and gives a step-by-step procedure for matching it against your wallet ledger and PG settlement files.

Expand Down Expand Up @@ -287,7 +287,7 @@ Each parent transaction's webhook payload (and dashboard detail page) carries tw
The key is **not present at all** on payments that never touched a wallet. Your reconciliation script should treat a missing key the same as an empty list.
:::

**Multi-wallet stacking.** If your setup supports more than one wallet provider (for example, Ottu Wallet + Qitaf), `customer_wallet_transactions` will contain one entry per provider that contributed funds. Sum the `amount` fields to get the total wallet portion of the payment. Ordering is not guaranteed — treat the array as a set.
**Multi-wallet stacking.** If your setup supports more than one wallet provider (for example, M-Wallet + Qitaf), `customer_wallet_transactions` will contain one entry per provider that contributed funds. Sum the `amount` fields to get the total wallet portion of the payment. Ordering is not guaranteed — treat the array as a set.

**Cross-currency reservations.** When the wallet's native ledger currency differs from the order currency (rare, but possible), the entry includes `wallet_amount_native` and `wallet_currency` fields alongside `amount` and `currency`. Reconcile against the wallet ledger using the native values; use `amount`/`currency` for the order-side math.

Expand Down Expand Up @@ -418,6 +418,6 @@ Export Accounts or Ledger data as **CSV** or **XLSX** for offline analysis and a

## What's Next?

- [Wallet for developers](/developers/payments/wallet/) — API and SDK integration.
- [M-Wallet for developers](/developers/payments/wallet/) — API and SDK integration.
- [Payment Management](/business/payment-management/) — viewing and managing all transactions.
- [Refund to Wallet (developer reference)](/developers/operations#refund-to-wallet) — the API behind the dashboard refund flow.
- [Refund to M-Wallet (developer reference)](/developers/operations#refund-to-wallet) — the API behind the dashboard refund flow.
8 changes: 4 additions & 4 deletions docs/developers/operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,9 @@ Settles authorized funds. Only applicable to `authorized` transactions. Supports

Returns funds to the customer. Applicable to `paid` or captured transactions. For authorized transactions, a capture must be completed first. Supports full or partial refund. Ottu offers an [approval feature](/business/operations/refund-void-access-control) for refunds, enabling a checker role to approve or reject requests.

##### Refund to Wallet {#refund-to-wallet}
##### Refund to M-Wallet {#refund-to-wallet}

Instead of returning funds through the original payment gateway, you can refund a payment directly to the customer's **wallet** balance. The customer can then spend that credit at any future Ottu checkout in the same currency. See [Wallet](/developers/payments/wallet/) for the full feature overview.
Instead of returning funds through the original payment gateway, you can refund a payment directly to the customer's **wallet** balance. The customer can then spend that credit at any future Ottu checkout in the same currency. See [M-Wallet](/developers/payments/wallet/) for the full feature overview.

**When to use:**

Expand Down Expand Up @@ -152,7 +152,7 @@ Wallet credits are immutable — a duplicate refund-to-wallet call creates a sec
| 409 | `idempotency_conflict` | Same Idempotency-Key reused with different payload |
| 422 | `validation_error` | Schema validation failed on the refund payload |

For the full wallet integration, including the read APIs and SDK behavior, see [Wallet](/developers/payments/wallet/). For the merchant dashboard workflow, see [Refund to Wallet (business docs)](/business/wallet#refund-to-wallet).
For the full wallet integration, including the read APIs and SDK behavior, see [M-Wallet](/developers/payments/wallet/). For the merchant dashboard workflow, see [Refund to M-Wallet (business docs)](/business/wallet#refund-to-wallet).

##### Void {#void}

Expand Down Expand Up @@ -180,7 +180,7 @@ Select the operation to see its example payload and the full interactive API sch
<ApiDocEmbed path="public-operations.api.mdx" />

</TabItem>
<TabItem value="refund-to-wallet" label="Refund to Wallet">
<TabItem value="refund-to-wallet" label="Refund to M-Wallet">

```json title="Example Request"
{
Expand Down
2 changes: 1 addition & 1 deletion docs/developers/payments/checkout-sdk/android.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -706,7 +706,7 @@ The SDK supports multiple instances of onsite checkout payments. Therefore, for
Fees are not displayed for onsite checkout instances due to the support of multiple card types through omni PG (multi-card) configurations. The presence of multiple payment icons also indicates this multi-card functionality.
:::

## Ottu Wallet
## M-Wallet {#ottu-wallet}

When a customer has wallet credit in the session currency, the SDK renders a **Wallet** payment method automatically — no init config is required. Pass the `customer_id` when creating the session, and either include `'wallet'` in `formsOfPayment` or omit `formsOfPayment` to show all available methods. Wallet is hidden for authorize-only sessions.

Expand Down
2 changes: 1 addition & 1 deletion docs/developers/payments/checkout-sdk/flutter.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -791,7 +791,7 @@ The SDK supports multiple instances of onsite checkout payments. Therefore, for
No fees are displayed for onsite checkout instances. This is due to the support for multiple multi-card (omni PG) configurations. The presence of multiple payment icons is used to indicate this multi-card feature.
:::

## Ottu Wallet
## M-Wallet {#ottu-wallet}

When a customer has wallet credit in the session currency, the SDK renders a **Wallet** payment method automatically — no init config is required. Pass the `customer_id` when creating the session, and either include `'wallet'` in `formsOfPayment` or omit `formsOfPayment` to show all available methods. Wallet is hidden for authorize-only sessions.

Expand Down
2 changes: 1 addition & 1 deletion docs/developers/payments/checkout-sdk/ios.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -679,7 +679,7 @@ The SDK supports multiple instances of onsite checkout payments. Therefore, for
Fees are not shown for onsite checkout instances because the system supports payments through multiple cards (omni PG). The multiple payment icons indicate the availability of different card options.
:::

## Ottu Wallet
## M-Wallet {#ottu-wallet}

When a customer has wallet credit in the session currency, the SDK renders a **Wallet** payment method automatically — no init config is required. Pass the `customer_id` when creating the session, and either include `'wallet'` in `formsOfPayment` or omit `formsOfPayment` to show all available methods. Wallet is hidden for authorize-only sessions.

Expand Down
4 changes: 2 additions & 2 deletions docs/developers/payments/checkout-sdk/web.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ The available options for `formsOfPayment` are:
- `stcPay`: A method where customers enter their mobile number and provide an OTP send to their mobile number to complete their payment.
- `urPay`: A method where customers enter their mobile number and provide an OTP send to their mobile number to complete their payment.
- `jazz`: Renders Ottu's onsite card form inline using the Jazz SDK, so customers enter their card details directly on the merchant page instead of being redirected. See [Onsite Checkout](#onsite-checkout) for setup details.
- `wallet`: The Ottu Wallet payment method that allows customers to spend their stored balance at checkout.
- `wallet`: The M-Wallet payment method that allows customers to spend their stored balance at checkout.

#### displayMode _`string`_

Expand Down Expand Up @@ -932,7 +932,7 @@ Checkout.init({

<figure><img src="/img/checkout-sdk/urpay%20Button.png" alt="urpay button in Web SDK" /><figcaption></figcaption></figure>

### Ottu Wallet
### M-Wallet {#ottu-wallet}

When a customer has wallet credit in the session currency, the SDK renders a **Wallet** payment method automatically — no init config is required. Pass the `customer_id` when creating the session, and either include `'wallet'` in `formsOfPayment` or omit `formsOfPayment` to show all available methods. Wallet is hidden for authorize-only sessions.

Expand Down
4 changes: 2 additions & 2 deletions docs/developers/payments/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ Process payments directly when you manage your own UI — Apple Pay buttons, Goo

[**Go to Native Payments →**](native-payments.md)

### Wallet (Stored Balance)
### M-Wallet (Stored Balance) {#wallet-stored-balance}

Refund payments to a customer's wallet balance instead of returning funds via the original gateway. Customers can spend wallet credit at any future Ottu checkout in the same currency — fully or partially alongside another payment method.

[**Go to Wallet →**](wallet/index.mdx)
[**Go to M-Wallet →**](wallet/index.mdx)

### Sandbox & Test Cards

Expand Down
4 changes: 2 additions & 2 deletions docs/developers/payments/payment-methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ Ottu offers SDKs and tools to speed up your integration. See [Getting Started](/

The Payment Methods API is a foundation for other Ottu APIs. It returns detailed information about each available payment method — supported operations, wallet integrations, currencies, and tokenization capabilities — giving you the data needed to create payment sessions with the right gateways.

:::tip Wallet as a payment method
When a customer has positive wallet balance in the session currency, a method with `type: "wallet"` appears in the response. See [Wallet](/developers/payments/wallet/) for the full integration.
:::tip M-Wallet as a payment method
When a customer has positive wallet balance in the session currency, a method with `type: "wallet"` appears in the response. See [M-Wallet](/developers/payments/wallet/) for the full integration.
:::

### Workflow
Expand Down
Loading
Loading