Integrate payments in under 15 minutes.
npm install @miniduckco/stashStart with the quickstart tutorial: docs/tutorials/quickstart.md.
import { createStash } from "@miniduckco/stash";
const stash = createStash({
provider: "paystack",
credentials: { secretKey: process.env.PAYSTACK_SECRET_KEY! },
});
const payment = await stash.payments.create({
amount: "25.00",
currency: "ZAR",
reference: "ORDER-100",
customer: { email: "buyer@example.com" },
urls: { returnUrl: "https://example.com/return" },
});
console.log(payment.redirectUrl);import { createStash } from "@miniduckco/stash";
const stash = createStash({
provider: "ozow",
credentials: {
siteCode: process.env.OZOW_SITE_CODE!,
apiKey: process.env.OZOW_API_KEY!,
privateKey: process.env.OZOW_PRIVATE_KEY!,
},
});
const payment = await stash.payments.create({
amount: "10.00",
currency: "ZAR",
reference: "ORDER-200",
customer: { email: "buyer@example.com" },
urls: { returnUrl: "https://example.com/return" },
});
console.log(payment.redirectUrl);import { createStash } from "@miniduckco/stash";
const stash = createStash({
provider: "payfast",
credentials: {
merchantId: process.env.PAYFAST_MERCHANT_ID!,
merchantKey: process.env.PAYFAST_MERCHANT_KEY!,
passphrase: process.env.PAYFAST_PASSPHRASE,
},
});
const payment = await stash.payments.create({
amount: "10.00",
currency: "ZAR",
reference: "ORDER-300",
customer: { email: "buyer@example.com" },
urls: { returnUrl: "https://example.com/return" },
});
console.log(payment.redirectUrl);const plan = await stash.subscriptions.plans.create({
name: "Monthly Retainer",
interval: "monthly",
amount: "5000.00",
});
const subscription = await stash.subscriptions.create({
customer: "CUS_xxxxxxxxxx",
plan: plan.planCode,
});
console.log(subscription.status);- Tutorials:
docs/tutorials/quickstart.md - How-to guides:
docs/how-to/README.md - Reference:
docs/reference/api.md - Explanation:
docs/explanation/architecture.md - Skills quick reference:
doc/skill.md
Runnable examples live in examples/:
- Index:
examples/README.md
Subscriptions let you create recurring billing in Paystack using plans and subscriptions.
import { createStash } from "@miniduckco/stash";
const stash = createStash({
provider: "paystack",
credentials: { secretKey: process.env.PAYSTACK_SECRET_KEY! },
});
const plan = await stash.subscriptions.plans.create({
name: "Monthly Retainer",
interval: "monthly",
amount: "5000.00",
amountUnit: "major",
currency: "ZAR",
invoiceLimit: 6,
});
console.log(plan.planCode);const subscription = await stash.subscriptions.create({
customer: "CUS_xxxxxxxxxx",
plan: "PLN_xxxxxxxxxx",
authorization: "AUTH_xxxxxxxxxx",
startDate: "2026-04-01T00:00:00.000Z",
});
console.log(subscription.subscriptionCode, subscription.status);const parsed = stash.webhooks.parse({
rawBody,
headers,
});
if (parsed.event.type === "subscription.created") {
const { subscriptionCode, customerCode, planCode } = parsed.event.data;
}
if (parsed.event.type === "invoice.payment_failed") {
const { invoiceCode, subscriptionCode, amount, currency } = parsed.event.data;
}Full example: examples/subscriptions-paystack.ts.
Structured logging (opt-in) emits canonical events with correlation IDs and safe metadata. See Structured logging.
The landing page + docs site lives in site/ and renders markdown directly from docs/.
Local dev:
npm install --prefix site
npm run dev --prefix site -- --host 0.0.0.0 --port 5173Docker preview:
docker build -t stash-site .
docker run --rm -p 5173:5173 stash-siteProviders
- Ozow
- Payfast
- Paystack
- Paygate
- Peach
Supported operations
- payments.create
- payments.verify (Ozow, Paystack; Payfast unsupported)
- webhooks.parse
- subscriptions.plans.create (Paystack)
- subscriptions.create (Paystack)
Notes:
- Ozow hash excludes
CustomerCellphoneNumber,Token, andGenerateShortUrl. - Ozow
AllowVariableAmount=falseis excluded from the hash. - Payfast signature excludes
setupand requires uppercase URL encoding. - Paystack subscriptions emit subscription + invoice webhook events.