Skip to content

Migrating from another aggregator ​

You use another aggregator today and want to move to SOAMTAR. Here is the recommended procedure to lose no transaction and keep your history.

Phase 1 — Preparation (D-30) ​

  1. Internal audit: how many transactions a month, which payment methods, which webhooks do you consume?
  2. SOAMTAR sandbox sign-up: create an account and check in the sandbox that all your features work.
  3. Reference mapping: prepare a script that maps your old → new merchant_ref values if they differ.
  4. Thorough sandbox testing: run at least 100 sandbox transactions through your critical paths.

Phase 2 — Coexistence (D-7 to D0) ​

Run both aggregators in parallel for a few days:

php
class PaymentRouter
{
    public function route(Order $order)
    {
        // Progressive switch-over
        $useSoamtar = $order->user_id % 100 < $this->soamtarRolloutPercent();

        return $useSoamtar
            ? app(SoamtarClient::class)->payments()->create(...)
            : app(LegacyClient::class)->initPayment(...);
    }

    private function soamtarRolloutPercent(): int
    {
        return (int) Cache::get('soamtar.rollout', 0);   // 0 → 100 progressively
    }
}

Raise the percentage day by day: 1% → 5% → 25% → 50% → 100%.

Phase 3 — Full switch-over (D0) ​

  1. Set soamtar.rollout to 100.
  2. Keep the legacy code running for another 30 days for late webhooks on old transactions.
  3. Tell your B2B merchants (if you are a platform) that fees and settlement cycles change.

Phase 4 — Decommissioning (D+30) ​

  1. Check that no webhook has reached the old endpoint for 7 days.
  2. Disable the legacy webhooks in the old aggregator's dashboard.
  3. Keep legacy read access for history (audits, support).
  4. Remove the routing code 30 days later.

Concept mapping ​

ConceptElsewhereSOAMTAR
Merchant referenceref_command, transaction_id, external_refmerchant_ref
Aggregator referencetoken, transaction_idid (UUID) + reference (TXN-…)
Webhook URLipn_url, notify_urlSet in Dashboard → Webhooks (per endpoint) or callback_url per transaction (receives every event of that transaction, same signature)
Server-side check/check, /verifyPOST /v1/payments/{id}/verify
Payment methodpayment_method, target_paymentprovider (orange_money_sl)
Amountinteger in minor units, or decimaldecimal string in leones, 2 decimals ("150.00")
Currencycurrencycurrency (SLE)
Payer phonevarious local formatsE.164: +232 followed by 8 digits
Free-form metadatacustom_field, metadatametadata (free JSON object)

Status mapping ​

Other aggregatorSOAMTAR
created, pendinginitiated (before submission) or pending (after submission)
complete, succeeded, paidsuccess
failed, errorfailed
canceled, cancelledcancelled
expired, timeoutexpired
refundedrefunded (or partially_refunded for a partial refund)

Webhook mapping ​

php
// Adapter: legacy webhook → internal SOAMTAR format
class LegacyWebhookAdapter
{
    public function toSoamtarFormat(array $legacyPayload): array
    {
        return [
            'event' => match ($legacyPayload['type_event'] ?? '') {
                'sale_complete' => 'payment.success',
                'sale_canceled' => 'payment.cancelled',
                'sale_failed' => 'payment.failed',
                default => null,   // intermediate legacy event: no SOAMTAR equivalent, ignore it
            },
            'created' => $legacyPayload['timestamp'] ?? now()->toIso8601String(),
            'data' => [
                'id' => $legacyPayload['ref_command'] ?? null,
                'reference' => $legacyPayload['token'] ?? null,
                'amount' => number_format((float) ($legacyPayload['item_price'] ?? 0), 2, '.', ''),
                'currency' => $legacyPayload['currency'] ?? 'SLE',
                'status' => $this->mapStatus($legacyPayload['status'] ?? ''),   // see the status table
                'metadata' => json_decode($legacyPayload['custom_field'] ?? '{}', true),
            ],
        ];
    }
}

Keeping the history ​

Keep a mapping table between old and new references:

php
Schema::create('migration_refs', function (Blueprint $table) {
    $table->uuid('id')->primary();
    $table->string('legacy_ref')->index();
    $table->string('soamtar_ref')->nullable()->index();
    $table->string('legacy_provider');
    $table->date('migrated_at');
});

Your support team can then find an old transaction with either reference.

Common risks ​

RiskMitigation
Double charge during coexistenceDeterministic Idempotency-Key based on your internal ID (never on the aggregator's reference)
Legacy webhooks after the switch-overKeep the legacy handler for 30 days: log, then skip
Refunding a legacy transactionDo it with the old aggregator (the transaction does not exist at SOAMTAR)
Fee difference → accounting gapCompare 7 days before and 7 days after, explain it to merchants
Mass failure at switch-overProgressive rollout (1% first), feature flag to roll back in 5 minutes

Migration help ​

Critical migration (more than 10,000 transactions a month)? support@so-amtar.com sets up:

  • one-to-one technical support;
  • a review of your webhook adapter;
  • real-time monitoring during the switch-over;
  • a post-migration report.

SOAMTAR Payment API Documentation.