Skip to content

Angular

Angular uses @sanwohq/web directly — no framework-specific wrapper needed. Angular’s dependency injection, lifecycle hooks, and template syntax work naturally with the createSanwo API.

Terminal window
npm i @sanwohq/web @sanwohq/paystack

Replace @sanwohq/paystack with whichever provider you need (@sanwohq/flutterwave, @sanwohq/razorpay, etc.).

import { Component, OnDestroy } from "@angular/core";
import { createSanwo, type SanwoInstance } from "@sanwohq/web";
import { paystackProvider } from "@sanwohq/paystack";
@Component({
selector: "app-root",
standalone: true,
template: `
<button (click)="pay()" [disabled]="isLoading">
{{ isLoading ? 'Processing…' : 'Pay ₦5,000' }}
</button>
`,
})
export class AppComponent implements OnDestroy {
private sanwo: SanwoInstance;
isLoading = false;
constructor() {
this.sanwo = createSanwo({
provider: paystackProvider,
publicKey: "pk_test_xxxxxxxxxxxx",
});
}
async pay() {
this.isLoading = true;
const result = await this.sanwo({
amount: 500000,
currency: "NGN",
customer: { email: "customer@example.com" },
});
this.isLoading = false;
if (result.status === "successful") {
console.log("Paid!", result.reference);
}
}
ngOnDestroy() {
this.sanwo.destroy();
}
}

Wrap createSanwo in an Angular service so any component can inject it.

import { Injectable, OnDestroy } from "@angular/core";
import { createSanwo, type SanwoInstance } from "@sanwohq/web";
import { paystackProvider } from "@sanwohq/paystack";
import type { CheckoutOptions, CheckoutResult } from "@sanwohq/types";
@Injectable({ providedIn: "root" })
export class SanwoService implements OnDestroy {
private sanwo: SanwoInstance;
constructor() {
this.sanwo = createSanwo({
provider: paystackProvider,
publicKey: "pk_test_xxxxxxxxxxxx",
});
}
checkout(options: CheckoutOptions): Promise<CheckoutResult> {
return this.sanwo(options);
}
ngOnDestroy() {
this.sanwo.destroy();
}
}

Create a new SanwoInstance when the provider changes and destroy the old one.

import { Component, OnDestroy } from "@angular/core";
import { createSanwo, type SanwoInstance } from "@sanwohq/web";
import { paystackProvider } from "@sanwohq/paystack";
import { flutterwaveProvider } from "@sanwohq/flutterwave";
@Component({
selector: "app-multi-provider",
standalone: true,
template: `
<select (change)="switchProvider($event)">
<option value="paystack">Paystack</option>
<option value="flutterwave">Flutterwave</option>
</select>
<button (click)="pay()">Pay</button>
`,
})
export class MultiProviderComponent implements OnDestroy {
private sanwo: SanwoInstance;
private providers = {
paystack: { provider: paystackProvider, key: "pk_test_xxx" },
flutterwave: { provider: flutterwaveProvider, key: "FLWPUBK-xxx" },
};
constructor() {
this.sanwo = this.buildInstance("paystack");
}
switchProvider(event: Event) {
const id = (event.target as HTMLSelectElement).value;
this.sanwo.destroy();
this.sanwo = this.buildInstance(id);
}
async pay() {
const result = await this.sanwo({
amount: 500000,
currency: "NGN",
customer: { email: "customer@example.com" },
});
console.log(result);
}
ngOnDestroy() {
this.sanwo.destroy();
}
private buildInstance(id: string): SanwoInstance {
const config = this.providers[id as keyof typeof this.providers];
return createSanwo({
provider: config.provider,
publicKey: config.key,
});
}
}

The checkout returns a CheckoutResult — a discriminated union on status:

const result = await this.sanwo({
amount: 500000,
currency: "NGN",
customer: { email: "customer@example.com" },
});
switch (result.status) {
case "successful":
// result.reference, result.providerResponse
break;
case "cancelled":
// user closed the checkout
break;
case "failed":
// result.error
break;
case "pending":
// result.reference — verify server-side
break;
}

Store your keys in Angular’s environment files:

export const environment = {
paystackPublicKey: "pk_test_xxxxxxxxxxxx",
};

A complete checkout form with scenario picker, form inputs, and result display:

import { Component, OnDestroy } from "@angular/core";
import { CommonModule } from "@angular/common";
import { FormsModule } from "@angular/forms";
import { createSanwo, type SanwoInstance } from "@sanwohq/web";
import type { CheckoutResult } from "@sanwohq/types";
import { paystackProvider } from "@sanwohq/paystack";
@Component({
selector: "app-checkout",
standalone: true,
imports: [CommonModule, FormsModule],
template: `
<div *ngIf="result">
<h2>{{ result.status === 'successful' ? 'Payment Successful' : 'Payment ' + result.status }}</h2>
<button (click)="result = null">Make another payment</button>
</div>
<form *ngIf="!result" (ngSubmit)="pay()">
<input [(ngModel)]="email" name="email" type="email"
placeholder="customer@example.com" required />
<input [(ngModel)]="amount" name="amount" type="number"
placeholder="1000.00" required min="1" step="0.01" />
<button type="submit" [disabled]="isLoading">
{{ isLoading ? 'Processing…' : 'Pay with Paystack' }}
</button>
</form>
<p *ngIf="error" style="color: red">{{ error }}</p>
`,
})
export class CheckoutComponent implements OnDestroy {
private sanwo: SanwoInstance;
email = "";
amount = "";
isLoading = false;
error: string | null = null;
result: CheckoutResult | null = null;
constructor() {
this.sanwo = createSanwo({
provider: paystackProvider,
publicKey: "pk_test_xxxxxxxxxxxx",
});
}
async pay() {
this.isLoading = true;
this.error = null;
try {
this.result = await this.sanwo({
amount: Math.round(parseFloat(this.amount) * 100),
currency: "NGN",
customer: { email: this.email },
});
} catch (err) {
this.error = err instanceof Error ? err.message : String(err);
} finally {
this.isLoading = false;
}
}
ngOnDestroy() {
this.sanwo.destroy();
}
}

See the Angular example app for a multi-provider demo with Paystack, Flutterwave, Razorpay, Monnify, and Interswitch.