Documentation Index Fetch the complete documentation index at: https://docs.fyatu.com/llms.txt
Use this file to discover all available pages before exploring further.
Unlock the African market for your SaaS product. FYATU enables you to accept subscription payments via local payment methods that your African customers actually use.
The Challenge
SaaS companies targeting African markets encounter:
Low card penetration - Most potential customers don’t have international cards
Payment failures - African cards often declined by international processors
Currency issues - Customers prefer paying in local currencies
Churn from payment failures - Involuntary churn due to payment method issues
Complex billing - Managing subscriptions across different payment methods
The FYATU Solution
Fyatu Payments Accept payments from 1M+ Fyatu users paying from their wallets
Subscription Billing Recurring payment support with retry logic
Multi-Currency Price in local currencies, receive USD
Payouts Pay African affiliates and partners to their Fyatu wallets
Subscription Integration
Create Subscription Payment
// New subscription signup
async function createSubscription ( user , plan ) {
const subscription = await Subscription . create ({
userId: user . id ,
planId: plan . id ,
amount: plan . price ,
currency: 'USD' ,
interval: plan . interval , // monthly, yearly
status: 'pending_payment'
});
// Create payment for first billing cycle
const collection = await fyatu . collections . create ({
amount: plan . price ,
currency: 'USD' ,
reference: `SUB- ${ subscription . id } -1` ,
description: ` ${ plan . name } - ${ plan . interval } subscription` ,
customer: {
email: user . email ,
name: user . fullName ,
externalId: user . id
},
metadata: {
subscriptionId: subscription . id ,
planId: plan . id ,
billingCycle: 1 ,
type: 'subscription'
},
redirectUrl: `https://app.yoursaas.com/subscription/activated` ,
webhookUrl: 'https://api.yoursaas.com/webhooks/fyatu'
});
return {
subscription ,
checkoutUrl: collection . checkoutUrl
};
}
Handle Subscription Activation
app . post ( '/webhooks/fyatu' , async ( req , res ) => {
const event = req . body ;
if ( event . type === 'collection.completed' ) {
const { subscriptionId , billingCycle } = event . data . metadata ;
if ( subscriptionId ) {
const subscription = await Subscription . findById ( subscriptionId );
if ( billingCycle === 1 ) {
// First payment - activate subscription
subscription . status = 'active' ;
subscription . activatedAt = new Date ();
subscription . currentPeriodStart = new Date ();
subscription . currentPeriodEnd = calculatePeriodEnd ( subscription . interval );
// Grant access
await grantPlanAccess ( subscription . userId , subscription . planId );
// Send welcome email
await sendWelcomeEmail ( subscription . userId );
} else {
// Renewal payment
subscription . currentPeriodStart = new Date ();
subscription . currentPeriodEnd = calculatePeriodEnd ( subscription . interval );
// Extend access
await extendAccess ( subscription . userId , subscription . currentPeriodEnd );
}
subscription . lastPaymentAt = new Date ();
subscription . lastPaymentId = event . data . id ;
await subscription . save ();
}
}
res . status ( 200 ). send ( 'OK' );
});
Renewal Reminders
// Daily job: send renewal reminders
async function sendRenewalReminders () {
const expiringIn3Days = await Subscription . findAll ({
where: {
status: 'active' ,
currentPeriodEnd: {
[Op.between]: [
addDays ( new Date (), 2 ),
addDays ( new Date (), 3 )
]
}
}
});
for ( const subscription of expiringIn3Days ) {
const user = await User . findById ( subscription . userId );
const plan = await Plan . findById ( subscription . planId );
// Create renewal payment link
const collection = await fyatu . collections . create ({
amount: plan . price ,
currency: 'USD' ,
reference: `SUB- ${ subscription . id } - ${ subscription . billingCycle + 1 } ` ,
description: ` ${ plan . name } renewal` ,
customer: { email: user . email },
metadata: {
subscriptionId: subscription . id ,
billingCycle: subscription . billingCycle + 1 ,
type: 'subscription_renewal'
},
expiresAt: subscription . currentPeriodEnd . toISOString ()
});
// Send reminder email with payment link
await sendRenewalReminder ( user . email , {
planName: plan . name ,
amount: plan . price ,
renewalDate: subscription . currentPeriodEnd ,
paymentLink: collection . checkoutUrl
});
}
}
Handle Expired Subscriptions
// Daily job: handle expired subscriptions
async function handleExpiredSubscriptions () {
const expiredSubscriptions = await Subscription . findAll ({
where: {
status: 'active' ,
currentPeriodEnd: { [Op.lt]: new Date () }
}
});
for ( const subscription of expiredSubscriptions ) {
// Grace period: 3 days
const gracePeriodEnd = addDays ( subscription . currentPeriodEnd , 3 );
if ( new Date () > gracePeriodEnd ) {
// Suspend access
subscription . status = 'past_due' ;
await subscription . save ();
await revokeAccess ( subscription . userId );
await sendSubscriptionExpiredEmail ( subscription . userId );
}
}
}
Pricing Tiers
Offer different pricing for African markets:
const plans = {
starter: {
name: 'Starter' ,
prices: {
USD: 29 ,
KES: 3500 , // ~20% discount
NGN: 35000 , // ~20% discount
CDF: 70000 // ~20% discount
}
},
professional: {
name: 'Professional' ,
prices: {
USD: 99 ,
KES: 12000 ,
NGN: 120000 ,
CDF: 240000
}
},
enterprise: {
name: 'Enterprise' ,
prices: {
USD: 299 ,
KES: 35000 ,
NGN: 350000 ,
CDF: 700000
}
}
};
// Get price for user's region
function getPriceForUser ( plan , userCurrency ) {
return plans [ plan ]. prices [ userCurrency ] || plans [ plan ]. prices . USD ;
}
One-Time Purchases
For lifetime deals or add-ons:
// Sell lifetime license
async function purchaseLifetimeLicense ( user , product ) {
const collection = await fyatu . collections . create ({
amount: product . price ,
currency: 'USD' ,
reference: `LTD- ${ user . id } - ${ product . id } ` ,
description: ` ${ product . name } - Lifetime License` ,
customer: {
email: user . email ,
externalId: user . id
},
metadata: {
productId: product . id ,
type: 'lifetime_deal'
}
});
return collection . checkoutUrl ;
}
// Purchase add-on
async function purchaseAddon ( user , subscription , addon ) {
const collection = await fyatu . collections . create ({
amount: addon . price ,
currency: 'USD' ,
reference: `ADDON- ${ subscription . id } - ${ addon . id } ` ,
description: `Add-on: ${ addon . name } ` ,
customer: { email: user . email },
metadata: {
subscriptionId: subscription . id ,
addonId: addon . id ,
type: 'addon'
}
});
return collection . checkoutUrl ;
}
Refunds
Handle subscription refunds:
async function refundSubscription ( subscriptionId , reason ) {
const subscription = await Subscription . findById ( subscriptionId );
const lastPayment = await Payment . findById ( subscription . lastPaymentId );
// Calculate prorated refund
const daysUsed = daysBetween ( subscription . currentPeriodStart , new Date ());
const totalDays = daysBetween ( subscription . currentPeriodStart , subscription . currentPeriodEnd );
const refundAmount = lastPayment . amount * ( 1 - daysUsed / totalDays );
const refund = await fyatu . refunds . create ( lastPayment . collectionId , {
amount: Math . round ( refundAmount * 100 ) / 100 ,
reason: reason ,
reference: `REF- ${ subscriptionId } - ${ Date . now () } `
});
// Cancel subscription
subscription . status = 'cancelled' ;
subscription . cancelledAt = new Date ();
await subscription . save ();
// Revoke access
await revokeAccess ( subscription . userId );
return refund ;
}
Affiliate Payouts
Pay African affiliates for referrals:
// Monthly affiliate payout
async function processAffiliatePayouts () {
const affiliates = await Affiliate . findAll ({
where: { pendingPayout: { [Op.gt]: 0 } }
});
for ( const affiliate of affiliates ) {
if ( affiliate . pendingPayout >= 50 ) { // Minimum payout
try {
const payout = await fyatu . payouts . create ({
amount: affiliate . pendingPayout ,
currency: 'USD' ,
reference: `AFF- ${ affiliate . id } - ${ Date . now () } ` ,
description: 'Affiliate commission payout' ,
recipient: {
accountId: affiliate . fyatuAccountId
},
metadata: {
affiliateId: affiliate . id ,
referralCount: affiliate . monthlyReferrals
}
});
affiliate . pendingPayout = 0 ;
affiliate . lastPayoutAt = new Date ();
affiliate . lastPayoutId = payout . id ;
await affiliate . save ();
} catch ( error ) {
console . error ( `Affiliate payout failed: ${ affiliate . id } ` , error );
}
}
}
}
Why Fyatu for SaaS?
Benefit Description 1M+ Users Access Fyatu’s growing user base across Africa Instant Payments Customers pay from their Fyatu wallet in seconds No Failed Cards Wallet payments don’t fail like card payments Reduced Churn Reliable payments reduce involuntary churn Affiliate Payouts Pay affiliates directly to their Fyatu wallets
Fyatu users can fund their wallets using various local payment methods, giving your SaaS access to African customers without managing multiple payment integrations.
Best Practices
Highlight Fyatu as a reliable payment method for your African customers. Users can fund their Fyatu wallet using their preferred local payment method and pay you seamlessly.
2. Send Payment Reminders
7 days before renewal
3 days before renewal
Day of renewal
After grace period expires
3. Reduce Involuntary Churn
Multiple payment attempts
Grace periods for expired subscriptions
Clear renewal payment links
Easy account management
Getting Started
Configure Collection App
Set up your app for subscription payments
Integrate
Add FYATU to your subscription flow
Test
Test subscription and renewal flows
Launch
Start accepting payments from African customers
Resources