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)
- Internal audit: how many transactions a month, which payment methods, which webhooks do you consume?
- SOAMTAR sandbox sign-up: create an account and check in the sandbox that all your features work.
- Reference mapping: prepare a script that maps your old → new
merchant_refvalues if they differ. - 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)
- Set
soamtar.rolloutto 100. - Keep the legacy code running for another 30 days for late webhooks on old transactions.
- Tell your B2B merchants (if you are a platform) that fees and settlement cycles change.
Phase 4 — Decommissioning (D+30)
- Check that no webhook has reached the old endpoint for 7 days.
- Disable the legacy webhooks in the old aggregator's dashboard.
- Keep legacy read access for history (audits, support).
- Remove the routing code 30 days later.
Concept mapping
| Concept | Elsewhere | SOAMTAR |
|---|---|---|
| Merchant reference | ref_command, transaction_id, external_ref | merchant_ref |
| Aggregator reference | token, transaction_id | id (UUID) + reference (TXN-…) |
| Webhook URL | ipn_url, notify_url | Set in Dashboard → Webhooks (per endpoint) or callback_url per transaction (receives every event of that transaction, same signature) |
| Server-side check | /check, /verify | POST /v1/payments/{id}/verify |
| Payment method | payment_method, target_payment | provider (orange_money_sl) |
| Amount | integer in minor units, or decimal | decimal string in leones, 2 decimals ("150.00") |
| Currency | currency | currency (SLE) |
| Payer phone | various local formats | E.164: +232 followed by 8 digits |
| Free-form metadata | custom_field, metadata | metadata (free JSON object) |
Status mapping
| Other aggregator | SOAMTAR |
|---|---|
created, pending | initiated (before submission) or pending (after submission) |
complete, succeeded, paid | success |
failed, error | failed |
canceled, cancelled | cancelled |
expired, timeout | expired |
refunded | refunded (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
| Risk | Mitigation |
|---|---|
| Double charge during coexistence | Deterministic Idempotency-Key based on your internal ID (never on the aggregator's reference) |
| Legacy webhooks after the switch-over | Keep the legacy handler for 30 days: log, then skip |
| Refunding a legacy transaction | Do it with the old aggregator (the transaction does not exist at SOAMTAR) |
| Fee difference → accounting gap | Compare 7 days before and 7 days after, explain it to merchants |
| Mass failure at switch-over | Progressive 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.