Appearance
Payment Provider Setup Guide
Overview
Upvendo integrates with exactly three payment providers in code: Stripe, Viva Wallet, and Square. There are no others (no Mollie, Adyen, or PayPal). The full list is defined in app/Enums/PaymentProviders.php:
text
viva -> Viva Wallet
stripe -> Stripe
square -> SquareThese three providers are not interchangeable alternatives for the same job. They each cover a different channel:
- Stripe powers online-ordering payments (card and Bancontact), via a Stripe connected account that Upvendo onboards for the merchant. Stripe is also used for the merchant's own subscription billing to the Upvendo platform, and — as Stripe Terminal — it is one of the two in-person options (see below).
- Viva Wallet powers kiosk / in-store terminal payments. It is the global kiosk default (
config/upvendo.phpkiosk_payment_providerdefaults toviva) and one of the two in-person options. - Square is a full POS integration. When a merchant has connected Square, Square Terminal replaces the profile's in-person provider for kiosk payments (
app/Services/Payment/PaymentService.phpisSquareIntegrated()branch). Square also processes its own payments, so a merchant on Square does not configure a separate Payment Profile.
A key structural fact that the back-office reflects: a single Payment Profile holds BOTH the Stripe (online-ordering) configuration and the in-person configuration together. They are two sections of one profile, not two separate providers you pick between. See the per-profile status helpers (has_stripe, stripe_ready, has_viva_wallet, viva_wallet_verified) in src/views/settings/PaymentsComponent.vue:75-115.
In-person provider (a permanent choice)
Creating a Payment Profile requires picking an in-person payment provider, and the choice cannot be changed afterwards. The dialog says so explicitly: "This choice is permanent. You won't be able to change the in-person provider after the profile is created."
| Option | Stored value | What it means |
|---|---|---|
| Viva Wallet | viva_wallet | Dedicated in-person account, independent from the Stripe online-ordering setup. |
| Stripe (Stripe Terminal) | stripe | One Stripe account for online and in-person — one onboarding, one dashboard, one payout schedule. |
The backend requires the field and accepts only those two values (app/Http/Requests/BackOffice/Settings/Payment/NewPaymentProfileRequest.php:47-54); the value is stored as the profile's processor and read back via getInPersonProvider() (app/RawModels/PaymentProfile.php:32,34,65-68).
At payment time PaymentService::processPaymentIntent() branches in this order (app/Services/Payment/PaymentService.php:211, branches at :233-249):
text
isSquareIntegrated() -> Square Terminal
transaction provider == 'stripe' -> Stripe Terminal
otherwise -> Viva WalletOnline ordering is unaffected by this choice — non-kiosk channels always resolve to the platform online provider, Stripe (app/Services/Payment/TransactionService.php:856,865-876).
Where to set things up (real routes)
| Page | Route | Source |
|---|---|---|
| Payment Profiles (Stripe + in-person provider) | /settings/payments | src/plugins/1.router/additional-routes.ts:15-18 |
| A single Payment Profile detail | /settings/payments/profiles/:id | src/plugins/1.router/additional-routes.ts:20-23 |
| Payouts for a profile | /settings/payments/payouts/:id | src/plugins/1.router/additional-routes.ts:25-28 |
| Platform billing (subscription to Upvendo) | /settings/billing | src/plugins/1.router/additional-routes.ts:35-38 |
| Billing subscriptions | /settings/billing/subscriptions/:id | src/plugins/1.router/additional-routes.ts:40-43 |
There is no
/settings/payments/stripe,/settings/payments/viva-wallet, or/integrations/squareroute. Stripe and Viva Wallet are configured inside a Payment Profile under/settings/payments. Square is connected from the "Add in-house channel" dialog (see the Square section below); its OAuth callback lands on/square,/square/success, or/square/error(typed-router.d.ts:125-127).
A note on prerequisites at integration connect time
When a merchant connects a third-party integration, the backend enforces only a default Branding profile for certain channels (Hendrickx, Vanhoutte, ShopCaisse, MplusKassa). Payment and Billing profiles are optional and can be set up later. This is explicit in app/Helpers/ThirdPartyIntegrationHelper.php:149-175 (comment at line 153):
// Payment and billing profiles are now optional - merchant can set them up later
So do not tell a merchant they must finish payment setup before connecting an integration — only Branding is required for the listed POS channels.
Stripe (online ordering + platform billing)
What it does
- Processes online-ordering payments. The supported online payment methods are Card and Bancontact (
app/Enums/PaymentMethodOptions.php). Card payments go throughcreateZestidooPaymentOrder(); Bancontact goes through a dedicated Stripe PaymentIntent or Checkout Session (app/Services/Orchestrators/OnlineOrderingOrchestrator.php:556-582). - Routes funds to the merchant via a Stripe connected account using destination charges (
stripe_account_idon the Payment Profile,app/RawModels/PaymentProfile.php;use_destination_chargesinconfig/services.php). - Handles the merchant's subscription billing to the Upvendo platform through a separate Billing Profile that stores a
stripe_customer_id(app/RawModels/BillingProfile.php), managed at/settings/billing.
iDEAL is not an online-ordering payment method in Upvendo. The only Stripe online payment methods in code are Card and Bancontact (
app/Enums/PaymentMethodOptions.php). The string "iDEAL" only appears inside the Viva Wallet kiosk fee/transaction code (app/Services/Common/VivaWalletService.php:319,527), not in the online checkout path.
How a merchant connects Stripe
Stripe uses Stripe Connect onboarding (Upvendo creates and onboards a connected account for the merchant), not a "connect your existing Stripe account" OAuth login. In the back-office:
- Go to Settings > Payments (
/settings/payments). - Create a Payment Profile via the New Profile dialog — the fields are name, country, and in-person payment provider (Viva Wallet or Stripe Terminal, rendered as selectable cards; this choice is permanent). Submit with Setup Profile. (
src/views/settings/Payments/NewPaymentProfileDialog.vue:149-227.) - Open the profile (
/settings/payments/profiles/:id) and start Stripe onboarding (the embedded Stripe onboarding flow —src/views/settings/Payments/PaymentProfileDetail.vue:335,494-495). - Complete Stripe's hosted verification (business details, bank account for payouts) inside the onboarding flow.
- The profile shows a Stripe status: not configured / setup incomplete / action required / ready (
src/views/settings/PaymentsComponent.vue:93-115).
Online-ordering payment flow
text
Customer places an online order
|
v
Order channel resolves payment_provider = stripe (config/upvendo.php zestidoo_payment_provider, default "stripe")
|
v
Customer selects a payment method
|
+---> Card: Stripe payment order created (createZestidooPaymentOrder)
+---> Bancontact: Stripe PaymentIntent or Checkout Session (createStripeBancontact*)
|
v
Stripe webhook confirms the payment (StripeWebhookOrchestrator)
|
v
Order marked paid; pushed to kitchen/POSImportant notes
- Stripe is the default online-ordering payment provider (
config/upvendo.phpzestidoo_payment_providerdefaults tostripe). - Funds settle to the merchant's connected Stripe account; the Upvendo platform takes its margin via destination charges (
config/services.php). - Deferred online methods also exist in the backend: Cash on Delivery (universal) and Invoice (Mplus-only) skip online payment collection entirely and settle POS-side (
app/Enums/PaymentMethodOptions.php). There is no separate "Cash on Pickup" toggle in this code — the deferred cash method isCashOnDelivery. - The Cash on Delivery toggle is hidden in the production back office.
src/views/online/online-ordering/forms/Checkout.vue:96-97wraps it inv-if="!isProduction"("hidden in production while thecod_available_forplumbing isn't fully wired"). In production, only Invoice appears under Online Ordering → Checkout → Payment methods, and only for Mplus tenants. Do not tell a production merchant to enable Cash on Delivery.
Viva Wallet / Stripe Terminal (in-person payments)
What it does
- Processes kiosk / in-store terminal payments. Which one runs is the profile's in-person provider (see above); Viva Wallet is also the global fallback default (
config/upvendo.phpkiosk_payment_providerdefaults toviva). - Viva Wallet drives a physical Viva terminal through the Viva Terminal API (
app/Services/Payment/PaymentService.phpprocessVivaWalletPayment()/initiateTerminalSale(); the integration is configured as a Viva ISV/OAuth setup inconfig/services.php). - Stripe Terminal drives a Stripe reader through
processStripeTerminalPayment()on the same service, using the merchant's existing Stripe connected account.
How a merchant connects Viva Wallet
Viva Wallet is configured inside a Payment Profile at /settings/payments — it is not a separate top-level page:
- Go to Settings > Payments (
/settings/payments) and open (or create) a Payment Profile. - Set up the Viva Wallet account through the in-profile dialogs (
src/views/settings/Payments/EditVivaWalletAccountDialog.vue,src/views/settings/Payments/VivaAccountIdDialog.vue). These walk the merchant to their Viva Wallet account to retrieve the required account identifier. - The profile shows a kiosk status: not configured / setup incomplete / ready (
src/views/settings/PaymentsComponent.vue:75-91). - The physical terminal is assigned per device. Unverified (kiosk-app side): the exact terminal-pairing UX on the kiosk device is outside these two repos.
Kiosk payment flow
text
Kiosk submits an order for payment
|
v
Channel = Kiosk -> payment_provider = the location profile's in-person provider
('stripe'), else config('upvendo.kiosk_payment_provider') (default "viva")
|
v
PaymentService.processPaymentIntent:
if isSquareIntegrated() -> Square Terminal
elif provider == 'stripe' -> Stripe Terminal
else -> Viva Wallet terminal
|
v
Terminal prompts "tap/insert card"
|
v
Terminal session status polled; payment confirmed
|
v
Order completed and synced to the POS/KDSImportant notes
- Viva Wallet is for terminal payments, not online checkout. Stripe Terminal is likewise in-person only — online ordering always goes through the Stripe online path.
- When the merchant has connected Square, kiosk payments route to Square Terminal regardless of the profile's in-person provider (
app/Services/Payment/PaymentService.phpisSquareIntegrated()branch). - Unverified (kiosk-app side): "tips on terminal", receipt printing, and firmware details are kiosk/terminal concerns and are not defined in the back-office or backend repos.
Square (full POS integration with built-in payments)
What it does
- A full in-house POS integration: catalog/menu sync with Square plus payment processing.
- Square processes its own payments, so no separate Payment Profile is needed. When Square is connected, the Payment Profiles page (
/settings/payments) hides the "new profile" button and shows a "Square is handling your payments" placeholder instead (src/views/settings/PaymentsComponent.vue:118-130and theis_square_integratedtemplate branch). - For kiosk payments, Square Terminal replaces Viva Wallet (
app/Services/Payment/PaymentService.phpisSquareIntegrated()->processSquarePayment()).
How a merchant connects Square
Square is connected via OAuth from the "Add in-house channel" dialog (the same place merchants connect Hendrickx, MplusKassa, Lightspeed K-Series, etc.), not from a dedicated /integrations/square route:
- Open the "Add in-house channel" flow and choose Square (
src/components/dialogs/channels/SquareChannelDialog.vue). - Authorize. The back-office calls the backend, which builds a Square OAuth authorization URL (
app/Http/Controllers/Api/BackOffice/SquareIntegrationController.php:45-133,initiateOAuth()). - Complete Square's OAuth consent. The callback returns to the back-office at
/square//square/success//square/error(typed-router.d.ts:125-127). - After connecting, Square catalog data syncs into Upvendo. (Sync direction and timing are managed by the Square integration service —
startSync/updateSyncTimeinSquareIntegrationController.)
The Square OAuth dialog notes that a default Billing profile is recommended (
channels.add_inhouse_channel.square.no_default_billing_profileinsrc/components/dialogs/channels/SquareChannelDialog.vue), but perThirdPartyIntegrationHelper::checkRequiredProfiles()only a Branding profile is enforced — billing remains optional at connect time.
Square flow
text
Square connected via OAuth (SquareIntegrationController.initiateOAuth)
|
v
Square catalog synced into Upvendo (Square integration service)
|
v
Kiosk payments route to Square Terminal (PaymentService.isSquareIntegrated -> processSquarePayment)
|
v
Square settles payouts to the merchant's bank accountImportant notes
- When Square is connected, the back-office treats Square as the payment handler: the Payment Profiles page hides profile creation and shows the Square placeholder (
src/views/settings/PaymentsComponent.vue). - For kiosk payments specifically, the
isSquareIntegrated()check inPaymentServiceroutes the terminal payment through Square instead of Viva Wallet (app/Services/Payment/PaymentService.php). - Unverified (storefront side): exactly how online-ordering checkout renders for a Square merchant (e.g. Square Web Payments SDK) is a storefront concern not present in these two repos. Do not assert it as fact.
Choosing the right setup
Online ordering (Card + Bancontact)
Set up Stripe inside a Payment Profile at /settings/payments. Card and Bancontact are the supported online methods. On Mplus locations, Invoice can additionally be offered as a deferred method. (Cash on Delivery exists in the backend but its toggle is hidden in the production back office — see the Stripe notes above.)
Kiosk / in-store terminal (no Square)
Pick the profile's in-person provider when you create it at /settings/payments: Viva Wallet (separate in-person account) or Stripe (Stripe Terminal — one Stripe account for online and in-person). The choice is permanent, so decide before creating the profile. Then finish that provider's onboarding inside the profile.
POS + payments in one (Square)
Connect Square from the "Add in-house channel" flow. Square then handles both its catalog sync and payments (kiosk terminal payments route to Square Terminal), and you do not create a separate Payment Profile.
Both online and kiosk (no Square)
A single Payment Profile covers both: Stripe for online ordering plus the profile's in-person provider (Viva Wallet or Stripe Terminal) for the kiosk terminal. They are two sections of the same profile, configured at /settings/payments. Choosing Stripe as the in-person provider means one Stripe account, one dashboard and one payout schedule for everything.
Common Issues
"Payment could not be processed"
- Confirm the relevant provider is set up in the Payment Profile (Stripe section ready for online; Viva Wallet section verified for kiosk) at
/settings/payments. - Verify the order's payment method is one of the supported methods (Card, Bancontact, or Invoice for Mplus).
- Check the provider dashboard (Stripe / Viva Wallet / Square) for declined transactions. Unverified (external dashboards): behavior in those external dashboards is outside Upvendo's codebase.
Kiosk terminal not responding
- Confirm the kiosk's payment provider: the Payment Profile's in-person provider (Viva Wallet or Stripe Terminal), or Square Terminal if the merchant is on Square.
- Verify the terminal is powered on and online, and assigned to the device.
- Unverified (kiosk-app side): terminal pairing, re-pairing, and firmware steps are kiosk/terminal concerns not defined in these repos.
Missing online payment methods at checkout
- Verify the Payment Profile's Stripe section is ready (
/settings/payments). - Remember only Card and Bancontact are online card-style methods; Invoice (Mplus-only) is a deferred method that settles POS-side. Cash on Delivery is not selectable in the production back office.
- For Square merchants: payment configuration is handled by the Square integration, not a Payment Profile — confirm the Square connection is active.