Payments with Plugsurfing's PSP
Note: The information on this page is only applicable when Plugsurfing is the acting payment provider (see Payment integration options for more details).
Plugsurfing is using Stripe as the PSP (Payment Service Provider).
Create a payment method
To create a Stripe credit card payment method there are a number of steps involved. This includes integrating with both Stripe and Plugsurfing to successfully complete this flow.
These are the steps involved in a payment method setup:
sequenceDiagram
participant EMP App
participant EMP BE
participant Drive API
participant Stripe
note over EMP App:1. Retrieve publishable API key, unless it's cached
EMP App->>EMP BE:GET - publishable API key
activate EMP App
activate EMP BE
note over EMP BE:Retrieve publishable API key, unless it's cached
EMP BE->>Drive API:GET - publishable API key
activate Drive API
Drive API-->>EMP BE:return
deactivate Drive API
EMP BE-->>EMP App:return
deactivate EMP BE
deactivate EMP App
note over EMP App:2. Create SetupIntent to be used with Stripe client-side library
EMP App->>EMP BE: GET - Create SetupIntent
activate EMP App
activate EMP BE
EMP BE->>Drive API:GET - Create SetupIntent
activate Drive API
Drive API-->>EMP BE:return
deactivate Drive API
EMP BE-->>EMP App:return
deactivate EMP BE
deactivate EMP App
note over EMP App:3.Use Stripe client-side library with the publishable API key
activate EMP App
EMP App->>EMP App: Collect PaymentMethod details
EMP App->>Stripe: Confirm SetupIntent with client-secret
activate Stripe
Stripe-->>EMP App:return
deactivate EMP App
deactivate Stripe
note over EMP App:4.Associate the newly created PaymentMethod.id to a user
EMP App->>EMP BE: POST - Create credit-card on user with PaymentMethod id
activate EMP App
activate EMP BE
EMP BE->>Drive API:GET - Create credit-card on user with PaymentMethod id
activate Drive API
Drive API-->>EMP BE:return
deactivate Drive API
EMP BE-->>EMP App:return
deactivate EMP BE
deactivate EMP App
- Get a publishable API key to be used towards the Stripe SDK. This is what makes Stripe identify the correct Stripe Platform, Meaning a Plugsurfing managed platform.
- Get a Stripe SetupIntent via Plugsurfing.
- Use Stripe client-side library to:
- Collect the Stripe PaymentMethod details.
- Confirm the SetupIntent with the PaymentMethod details, client-secret, and the publishable API key.
- Post the PaymentMethod id with the associated user to Plugsurfing in order to finalize the payment method setup.
EU regulation: Co-badged cards compliance
Some payment cards support co-badging, meaning they can be processed through multiple networks (e.g., Visa, Cartes Bancaires).
To comply with EU regulations, your app must allow users to select their preferred network when creating a payment method.
Stripe documentation:
Note:
Plugsurfing platform doesn't allow updating their card network of an existing credit-card.
If a user wants to use another network, they would need to create a new credit-card payment-option.
Publishable API key
The Stripe publishable API key is a client-side identifier provided by Stripe that is used to interact with Stripe's APIs securely from the frontend of your application.
In the case of libraries like the React Native Stripe library, the publishable key is essential for initializing the library and enabling your app to create and manage payment elements, such as payment forms.
Plugsurfing or Stripe may at any time roll the publishable API key.
Below are some important implementation recommendations to ensure seamless integration.
- To avoid unnecessary downtime, when Stripe rejects a request which includes the publishable API key (with an HTTP
401 Unauthorized), the EMP app needs to refresh the key by invoking the relevant Drive API endpoint. - For optimal performance, we recommend caching the Stripe publishable API key. The publishable key is static and does not change frequently, so re-fetching it on every request can add unnecessary latency to your application.
Successful payments
When a payment is successful the session will be in this type of state:
sessionStatus will be COMPLETE
balance_status will be PAID
balanceAmountMinor will be 0
Failed payments
When a payment fails, the session balance status and amount will reflect if the session was paid or not.
Sometimes there can be a partially paid session.
Ex: The session price is €40. We reserve €30, we then capture the reserved $30, but we are unable to capture the outstanding €10.
Plugsurfing will automatically retry unpaid sessions up to 7 days.
EMP initiated retry
The EMP can retry a failed payment using the appropriate endpoint. However, in most cases, payment failures are not caused by technical errors but by issues such as insufficient funds, expired cards, or similar problems. Frequent retries are unlikely to resolve these issues. Instead, it is recommended to notify the end customer to ensure they have sufficient funds or to use a different payment method before attempting the payment again.
3DS Authentication
Even when the payment is labeled as an "off session" payment, the banks will sometimes trigger a 3DS authentication challenge before they accept the payment. This is also a cause of session payment failures since when the session payment takes place, there is no customer available to perform the authentication.
When this happens, the EMP needs to bring the customer back to the app, retry the payment, and if the bank again triggers a 3DS challenge, the EMP must have support for bringing the customer through a 3DS authentication flow.
Plugsurfing will return a Stripe client secret which the EMP must use when initiating the 3DS flow. Consult the Stripe documentation for more information.
Credit-card reservation during session start
When requesting a session start, Plugsurfing will reserve a specified amount on the user’s credit card during authorization. This reservation ensures that the card is valid and has sufficient funds to cover the configured amount. The reserved amount is held on the user’s card and is not charged until the session is completed.
If the reservation has not been captured within a number of hours, then Plugsurfing will automatically release the reservation. Please note that it may take additional time for the user’s bank to process the release and make the funds available again.
Requirements for making a reservation
- The reservation amount per country must be configured in the Plugsurfing Backoffice.
- During a session start request, the user must have a
CREDIT_CARDpayment method.
3DS challenge during reservation
The issuing bank might trigger a 3D Secure (3DS) authentication challenge during the reservation process. If this happens, the EMP must handle the 3DS flow to complete the reservation.
In such cases, the session start request may return a Stripe client secret instead of a session id. The EMP must then use this client secret to initiate the 3DS authentication flow with the user.
3DS Challenge Handling Modes
Depending on the configuration in the Plugsurfing Backoffice, the reservation part of the session authorization flow will act differently on a 3DS challenge from the issuing bank:
ALWAYS_ALLOW(default): Automatically be allowed.ALWAYS_DENY: Automatically be denied.CHALLENGE: Initially denied, while returning a Stripe client secret to the EMP. The EMP should use this client secret to complete the 3DS authentication flow with the user in their frontend. Once the user completes the challenge successfully, the reservation will be completed, and the EMP must retry the session start request!
Below is flow for CHALLENGE mode.
sequenceDiagram
participant EMP App
participant EMP BE
participant Drive API
participant Stripe
note over EMP App: 1. Start a remote charging session
EMP App->>EMP BE: POST - Start a remote charging session
activate EMP App
activate EMP BE
EMP BE->>Drive API: POST - Start a remote charging session
activate Drive API
Drive API->>Stripe: During authorization: make reservation
activate Stripe
Drive API-->>Stripe: return threeDSClientSecret
deactivate Stripe
Drive API-->>EMP BE: On CHALLENGE mode: return threeDSClientSecret
deactivate Drive API
EMP BE-->>EMP App: return threeDSClientSecret
deactivate EMP BE
deactivate EMP App
note over EMP App: 2.Use Stripe client-side library with the 3DS client-secret
EMP App->>Stripe: Prompt user to complete the 3DS challenge
activate EMP App
activate Stripe
Stripe-->>EMP App: return
deactivate EMP App
Stripe-->>Drive API: Webhook telling us the challenge was completed
activate Drive API
deactivate Drive API
deactivate Stripe
note over EMP App:3. On successful 3DS challenge: retry starting the remote charging session
EMP App->>EMP BE: POST - Start a remote charging session
activate EMP App
activate EMP BE
EMP BE->>Drive API:POST - Start a remote charging session
activate Drive API
Drive API-->>EMP BE:return sessionId (webhook already confirmed)
deactivate Drive API
EMP BE-->>EMP App:return sessionId
deactivate EMP BE
deactivate EMP App
Example code
Typescript/React-native pseudocode example for handling a reservation 3DS challenge during session start.
Note: Do not take this as a copy-paste solution, but rather as inspiration on how to implement this in your app.
import { handleNextAction } from '@stripe/stripe-react-native';
async function startSession(userId: string, connectorId: string) {
try {
const response = await callStartSessionAPI(userId, connectorId);
if (response.threeDSClientSecret) {
// 3DS challenge required
console.log("3DS challenge required, handling via Stripe...");
const result = await handleNextAction(response.threeDSClientSecret);
if (result.error) {
// Stripe challenge failed — rethrow
throw new Error(`3DS authentication failed: ${result.error.message}`);
}
console.log("3DS challenge completed, retrying session start...");
// Retry session start after successful 3DS flow
return await callStartSessionAPI(userId, connectorId);
}
// No 3DS challenge, session started normally
return response;
} catch (err) {
console.error("Failed to start session:", err);
throw err;
}
}Debt
What do we mean by the term debt? If one looks at for example the session balanceStatusthere is an attribute named DEBT. Debt means the customer have not paid the session in full and have a negative balance. The balance is extracted from the session attribute balanceAmountMinor.
What are the customer consequences of having debt?
The customer will be blocked from starting yet another session until the outstanding amount is fully paid.
Receipts
When the payment succeeds. There will be a session receipt available.
Refunds
Refunds will be handled manually between the EMP support and the Plugsurfing support. Plugsurfing are able to do as many (partial) refunds as may be needed. Refunds won't affect the balanceAmountMinor.
When a session is refunded, even if partially refunded, the balanceStatus will be in state REFUND. This is how the Plugsurfing platform currently handles this. Since refunds are a manual process, the details, remaining refundable amount, can be sorted out between humans.
Updated about 2 months ago