Skip to main content
Version: Next

Arguably the most important feature of the SDK is to execute payments between the business user and customer over BLE. In the payment use case, in order to make a successful payment, you will need:

  • A logged-in business user
  • A logged-in customer with at least one valid payment instrument

The payment flow works as follows:

  1. Customer starts advertising via BLE and listens for payment events
  2. Business side creates a payment instance and scans for BLE connections
  3. Information exchange occurs between business and customer devices once connected
  4. Payment request is sent by the business user and can be accepted or cancelled by the customer
  5. Payment acceptance is sent from the customer to the business user if approved
  6. Payment execution happens on both sides with the backend

Offline scenarios:

  • If one side is offline, the online side handles the payment and shares results
  • If both are offline, payment requires pre-authorization (experimental feature)

Let's check each step in more detail.

Customer: Receiving Payments

The flow has two steps. You need to connect to the business side via BLE and register as a payment receiver to be notified about any payment requests coming from the business side.

Important: The callback closure handles multiple events and can receive multiple payments while active. Keep this closure alive while you want to receive payments.

The usual flow of these events for a payment looks like:

  1. receivedPayment
  2. finishedPaymentwhen the payment is concluded with success
    alternate payment outcomes:
    1. cancelledin case the payment is cancelled by either side this indicates the end of a payment
    2. error in case a payment or an established payment connection fails
    3. otherPaidin case the received payment was paid by another payee
  3. disconnected when the connection is closed
vipaso.payment.customer.receivePaymentRequestsViaBLE { paymentEvent in
switch paymentEvent {
case .cancelled:
// Handle the cancellation
self?.handlePaymentCancellation()
case .error(let error):
// Handle the error case
self?.handlePaymentError(error)
case .finishedPayment(let paymentResult):
// Handle the case the payment is finished
self?.handleFinishedPayment()
case .receivedPayment(let paymentRequest):
// display payment for the user
self?.handlePayment(paymentRequest: paymentRequest)
case .otherPaid:
// Handle the case if another wallet paid the payment
self?.handleOtherPayment()
case .disconnected:
// Handle the case if another wallet paid the payment
self?.handleDisconnected()
}
}
}

Example screenshots from our development application:

Business: Requesting Payments

Business users can send payment requests via BLE to nearby customer devices:

vipaso.payment.business.sendPaymentRequestViaBLE(
amount: "25.50",
currency: "USD",
paymentReference: "order-123" // Optional reference
) { [weak self] result in
switch result {
case .success(let response):
self?.handlePaymentResponse(response)
case .failure(let error):
self?.handlePaymentError(error)
}
}

Accepting the Payment

With instruments managed by Vipaso

After the user has reviewed the payment details and selected a payment instrument, they can accept and execute the payment:

let request = VipasoPaymentAcceptanceRequest(
paymentID: paymentID,
amount: amount,
tip: tip,
currency: currency,
instrumentID: instrumentID,
createdAt: Date().iso8601String,
isDelegatedInstrumentID: false // Set to true for delegated instruments
)

vipaso.payment.customer.acceptPayment(request: request) { [weak self] result in
switch result {
case .success(let response):
self?.handleAcceptanceSuccess(response)
case .failure(let error):
self?.handleAcceptanceFailure(error)
}
}

With client Delegated instruments

The VipasoPaymentAcceptanceRequest.isDelegatedInstrumentID property also indicates if the payment is being done with a delegated instrument.

let request = VipasoPaymentAcceptanceRequest(
paymentID: paymentID,
amount: amount,
tip: tip,
currency: currency,
instrumentID: instrumentID,
createdAt: Date().iso8601String,
isDelegatedInstrumentID: true
)

vipaso.payment.customer.acceptPayment(request: request) { result in
switch result {
case .success(let request):
promise(.success(request))
case .failure(let error):
promise(.failure(.generic(error: error)))
}
}

NOTE: The acceptance method call is the same. Only the input model changes.

Example screenshots from our development application:

Cancelling the payment

Customer side cancellation:

With this call the customer indicates to the business user that the it does not pay the received payment. IMPORTANT: When this call succeeds the customer will not receive this payment from the business side anymore.

vipaso.payment.customer.cancelPayment(paymentID: paymentID) { [weak self] result in
switch result {
case .success(let response):
self?.handleCancellationSuccess(response)
case .failure(let error):
self?.handleCancellationFailure(error)
}
}

Business side cancellation:

With this call the business user indicates to all the connected customers that it cancels the payment so they can not pay it anymore. IMPORTANT: This is a fire and forget call.

vipaso.payment.business.cancelPayment()

Stop Receiving Payments

When you no longer need to receive payments, you can stop the payment reception:

vipaso.payment.customer.stopBLE()