diff --git a/client/src/services/bnpl/constants.ts b/client/src/services/bnpl/constants.ts
index b400268..1fe7daf 100644
--- a/client/src/services/bnpl/constants.ts
+++ b/client/src/services/bnpl/constants.ts
@@ -2,18 +2,12 @@
* When true, the BNPL domain is served by the in-memory mock (`apis/mockApi.ts`) behind the `BnplApi`
* seam.
*
- * **Mock is primary this phase.** b12 ships the eligibility/initiate/webhook/settle endpoints server-side,
- * but the D1–D5 checkout cannot run real end-to-end from the client yet:
- * - the accepted request being financed comes from the **mock-primary** `bookingRequests` store (f7), so a
- * real `initiate` would reference an id that exists only in memory (same reason f9 payment is mock-primary);
- * - the contract serves **no provider/plan options** (D1/D2), **no repayment schedule** (D4 — the contract
- * explicitly does not model the customer's repayment schedule), and **no provider-reported installment
- * status** for the Wallet (D5) → REQ-022/023/024;
- * - nothing fires the provider webhook in dev, so a real order would never settle.
- * The mock closes the loop: the settle (down-payment cleared) converts the f7 request, inserts a **confirmed**
- * booking into the f8 store (the SAME bridge f9 uses — a settled BNPL order is a card payment net-of-fee),
- * and seeds a Wallet installment plan — so C6 → D1 → … → D4 → confirmation → D5 demos end-to-end. Flip to
- * `false` once the upstream domains are real and REQ-022/023/024 land — no hook/component change.
+ * **Mock is still primary** (blocker-phase 05 landed Bug A — the seeded BNPL gateway — but not Bug B):
+ * the D1 wizard hard-gates on `getBnplOptions` (`GET checkout_bnpl/options/{id}`), and `getBnplSchedule` /
+ * `getWalletInstallments` have no server counterpart either — `CheckoutBnplController` only ever grew
+ * `eligibility` / `initiate` / `GET {id}` / `by_request/{id}` (REQ-022/023/024 never landed). Flipping this
+ * flag today trades the old "mock store ages out" failure for an immediate `isError` on D1. Flip once those
+ * three endpoints exist server-side — no hook/component change needed at that point.
*/
export const USE_BNPL_MOCK = true;
diff --git a/mvp/blockers.md b/mvp/blockers.md
index d9020b7..b378d82 100644
--- a/mvp/blockers.md
+++ b/mvp/blockers.md
@@ -21,8 +21,13 @@ Effort is a rough size, not a schedule: **S** = small/contained, **M** = a real
- **The 30-minute payment countdown can lie.** Booking deadlines are stored without a timezone, so the timer
a customer sees can silently show hours more time than they actually have, and expire while they still
think they're fine. *(Effort: S)*
-- **Installment (buy-now-pay-later) payments don't work at all.** No installment plan is ever actually set up
- behind the scenes, so every attempt fails immediately. *(Effort: S–M, two related issues)*
+- **Installment (buy-now-pay-later) payments don't work at all.** The behind-the-scenes setup that lets an
+ installment plan get created at all is now fixed — but the on-screen wizard still can't be turned on to use
+ it: three of its screens (choosing a plan, seeing the repayment schedule, and the "my installments" wallet
+ view) call server endpoints that were never actually built, so switching off the fake demo data today would
+ make the first screen show an error immediately instead of the outdated-mock failure it shows now. Those
+ three endpoints need to be built before this can be turned on for real. *(Effort: S–M, one part fixed, one
+ part bigger than first scoped)*
- **Refunds are demo-only today.** The refund screens read fake, disconnected sample data; turning that off
today would show a refund of the wrong amount (off by a factor of 100) for any real cancellation.
*(Effort: M)*
diff --git a/server/src/Infrastructure/Baya.Infrastructure.Persistence/ServiceConfiguration/ServiceCollectionExtensions.cs b/server/src/Infrastructure/Baya.Infrastructure.Persistence/ServiceConfiguration/ServiceCollectionExtensions.cs
index db40128..e86085b 100644
--- a/server/src/Infrastructure/Baya.Infrastructure.Persistence/ServiceConfiguration/ServiceCollectionExtensions.cs
+++ b/server/src/Infrastructure/Baya.Infrastructure.Persistence/ServiceConfiguration/ServiceCollectionExtensions.cs
@@ -8,6 +8,7 @@ using Baya.Application.Contracts.Payments;
using Baya.Application.Contracts.Persistence;
using Baya.Application.Contracts.Search;
using Baya.Application.Contracts.SupportAlerts;
+using Baya.Domain.Entities.Bnpl;
using Baya.Domain.Entities.Payments;
using Baya.Infrastructure.Persistence.Interceptors;
using Baya.Infrastructure.Persistence.Repositories.Common;
@@ -128,27 +129,43 @@ public static class ServiceCollectionExtensions
///
/// Idempotently seeds one active standard payment gateway so the b10 card rail has a selectable
- /// provider out of the box. config_json is encrypted at rest by the EF converter on save (so it
- /// must go through the DbContext, not HasData). Real merchant credentials come from appsettings /
- /// environment per deployment — this sandbox row is non-secret and only enables the local/dev flow.
+ /// provider out of the box, plus one active bnpl gateway (balinyaar, the in-house
+ /// net-of-fee provider per money.md §6 — needs no external credentials) so b12's eligibility/initiate
+ /// handlers have a gateway to select instead of short-circuiting. config_json is encrypted at rest
+ /// by the EF converter on save (so it must go through the DbContext, not HasData). Real merchant
+ /// credentials come from appsettings / environment per deployment — these sandbox rows are non-secret and
+ /// only enable the local/dev flow.
///
public static async Task SeedPaymentGatewaysAsync(this WebApplication app)
{
await using var scope = app.Services.CreateAsyncScope();
var context = scope.ServiceProvider.GetRequiredService();
- if (await context.Set().AnyAsync(g => g.Type == PaymentGatewayType.Standard))
- return;
-
- context.Set().Add(new PaymentGateway
+ if (!await context.Set().AnyAsync(g => g.Type == PaymentGatewayType.Standard))
{
- ProviderCode = "zarinpal",
- Type = PaymentGatewayType.Standard,
- DisplayName = "ZarinPal (sandbox)",
- ConfigJson = "{\"merchantId\":\"00000000-0000-0000-0000-000000000000\",\"baseUrl\":\"https://sandbox.zarinpal.com\",\"sandbox\":true}",
- IsActive = true,
- Priority = 0
- });
+ context.Set().Add(new PaymentGateway
+ {
+ ProviderCode = "zarinpal",
+ Type = PaymentGatewayType.Standard,
+ DisplayName = "ZarinPal (sandbox)",
+ ConfigJson = "{\"merchantId\":\"00000000-0000-0000-0000-000000000000\",\"baseUrl\":\"https://sandbox.zarinpal.com\",\"sandbox\":true}",
+ IsActive = true,
+ Priority = 0
+ });
+ }
+
+ if (!await context.Set().AnyAsync(g => g.Type == PaymentGatewayType.Bnpl))
+ {
+ context.Set().Add(new PaymentGateway
+ {
+ ProviderCode = BnplProviderCodes.Balinyaar,
+ Type = PaymentGatewayType.Bnpl,
+ DisplayName = "Balinyaar BNPL",
+ ConfigJson = "{\"sandbox\":true}",
+ IsActive = true,
+ Priority = 0
+ });
+ }
await context.SaveChangesAsync();
}