Skip to content

Commit

Permalink
Merge pull request #2382 from SwedbankPay/feature/sdk-25_web_view_bas…
Browse files Browse the repository at this point in the history
…ed_instruments_from_native_session

SDK-25: Web View based instruments from Native session
  • Loading branch information
alleus authored Dec 4, 2024
2 parents e1207bb + 290a7d8 commit c03c88b
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -179,10 +179,11 @@ PaymentSession.paymentSessionState.observe(viewLifecycleOwner) { paymentState ->
## Android Present Payment

You are now ready to present the payment UI. You can ask the payment session
class to create a `PaymentFragment` for web view based payments:
class to create a `PaymentFragment` for web view based payments. We request a
menu mode web view, without any restriction of instruments.

```kotlin
paymentSession.createPaymentFragment()
paymentSession.createPaymentFragment(mode = SwedbankPayPaymentSessionSDKControllerMode.Menu(null))
```

After getting back the `PaymentFragment` instance , you can present it in a way
Expand Down Expand Up @@ -227,7 +228,7 @@ class MainActivity : AppCompatActivity() {
Log.d("SwedbankPay", "Payment Session Fetched")

// Reqeust a web based payment fragment instance
paymentSession.createPaymentFragment()
paymentSession.createPaymentFragment(mode = SwedbankPayPaymentSessionSDKControllerMode.Menu(null))
}

is PaymentSessionState.ShowPaymentFragment -> {
Expand Down Expand Up @@ -416,10 +417,11 @@ func paymentSessionCanceled() {
```

You are now ready to present the payment UI. You can ask the payment session
class to create a `SwedbankPaySDKController` for web view based payments.
class to create a `SwedbankPaySDKController` for web view based payments. We
request a menu mode web view, without any restriction of instruments.

```swift
paymentSession.createSwedbankPaySDKController()
paymentSession.createSwedbankPaySDKController(mode: .menu(restrictedToInstruments: nil))
```

The `SwedbankPaySDKController` instance is returned via the
Expand Down Expand Up @@ -455,7 +457,7 @@ class ViewController: UIViewController, SwedbankPaySDKPaymentSessionDelegate {

func paymentSessionFetched(availableInstruments: [SwedbankPaySDK.AvailableInstrument]) {
print("Available Instruments Fetched")
paymentSession.createSwedbankPaySDKController()
paymentSession.createSwedbankPaySDKController(mode: .menu(restrictedToInstruments: nil))
}

func sessionProblemOccurred(problem: SwedbankPaySDK.ProblemDetails) {
Expand Down
190 changes: 131 additions & 59 deletions checkout-v3/modules-sdks/mobile-sdk/native-payments.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,8 @@ PaymentSession.paymentSessionStatenativePaymentState.observe(viewLifecycleOwner)

The `AvailableInstrumentsFetched` state contains a list of `AvailableInstrument`
on the `availableInstruments` parameter. This list of payment methods that
can be used for native payments. These instrument object also contain saved
payment method data. This could be saved credit cards or known MSISDN for the
user.
can be used for payments. These instrument object also contain saved payment
method data. This could be saved credit cards or known MSISDN for the user.

## Swish

Expand Down Expand Up @@ -241,9 +240,9 @@ the MSISDN string before making the native Swish payment attempt.

When requesting local start, the SDK will automatically launch the Swish app on
the local device. If an error occurred when starting the app, your app will be
informed by the problem by receiving the `SdkProblemOccurred` payment state,
where the `problem` parameter will be
`PaymentSessionProblem.ClientAppLaunchFailed`.
informed by the problem by receiving the `SessionProblemOccurred` payment state,
where the `problem` parameter will have the `type` value
`"https://api.payex.com/psp/errordetail/paymentorders/clientapplaunchfailed"`.

## Saved Credit Cards

Expand Down Expand Up @@ -590,9 +589,8 @@ func paymentSessionCanceled() {
The `availableInstruments` array provided in the
`paymentSessionFetched(_:)` delegate method is an array of
`SwedbankPaySDK.AvailableInstrument` with information of payment methods that
can be used for native payments. These instrument object also contain saved
payment method data. This could be saved credit cards or known MSISDN for the
user.
can be used for payments. These instrument object also contain saved payment
method data. This could be saved credit cards or known MSISDN for the user.

## Swish

Expand Down Expand Up @@ -621,9 +619,9 @@ the MSISDN string before making the native Swish payment attempt.

When requesting local start, the SDK will automatically launch the Swish app on
the local device. If an error occurred when starting the app, the SDK will call
the `sdkProblemOccurred(problem:)` delegate method, and provide the
`SwedbankPaySDK.PaymentSessionProblem.clientAppLaunchFailed` problem as
parameter.
the `sessionProblemOccurred(problem:)` delegate method, and provide a problem
with type `type` parameter set to
`"https://api.payex.com/psp/errordetail/paymentorders/clientapplaunchfailed"`.

## Saved Credit Cards

Expand Down Expand Up @@ -834,11 +832,6 @@ handled in the following ways:
callback function on Android that you can call to retry the underlying API
call. You should inform the user of the error and give the option to either
abort the payment session or retry the call.
* `ClientAppLaunchFailed` informs you that the SDK have attempted to launch an
external app (such as Swish) and that it failed to do so. You should inform
the user of the problem and give them the option to either make a new
attempt (with any available payment method) or to abort the whole payment
session.
* `InternalInconsistencyError` is the result of an logic inconsistency problem
in the SDK. An example of this would be to call `abortPaymentSession()`
before `startPaymentSession()`. If you receive this error during
Expand All @@ -862,6 +855,127 @@ the payment order with on your backend.
If you get this error in your production app, you should inform the user of
a generic technical error and restart the checkout process.

## Alternative checkout flows

Depending on the checkout flow in your app, there might be a need to implement
the Native Payment feature slightly different.

### Payment menu fallback

You might want to give the user an option to make payments in the regular, web
view based, payment menu using the SDK. You can easily achieve this by reusing
the same session and present the web view based SDK UI as a fallback depending
on the user choice. You can also use this functionality to directly present one
specific instrument for the user in the web view. This supports all instruments,
including the instruments not supported as native payments, such as Invoice
payments.

In the `availableInstruments` array you receive from the session, you will find
instruments indicated as "Web Based". These don't have a corresponding
`PaymentAttemptInstrument`, and can therefore not be used to make native payment
attempts. They can instead be used to configure the web view based SDK UI.

An example flow for presenting a "More payment methods" for the user:

```mermaid
sequenceDiagram
participant App
participant SDK
App ->> SDK: fetchPaymentSession()
activate SDK
App ->> App: Show loading indicator
SDK ->> App: paymentSessionFetched()
deactivate SDK
App ->> App: Present available payment methods to user
note over App: Include custom option<br/>"More payment methods"
opt If a native payment instrument is picked by user
App ->> SDK: makeNativePaymentAttempt()
activate SDK
App ->> App: Show loading indicator
SDK ->> App: paymentComplete()
deactivate SDK
end
opt If "More payment methods" option is picked by user
App ->> SDK: Create payment menu
activate SDK
note over App, SDK: Android: createPaymentFragment()<br/>iOS: createSwedbankPaySDKController()
SDK ->> App: Show payment menu
note over App, SDK: Android: ShowPaymentFragment<br/>iOS: showSwedbankPaySDKController()
App ->> App: Present web based payment menu
SDK ->> App: paymentComplete()
deactivate SDK
end
```

You request a web view based payment menu, and specify the mode, using the SDK
with an ongoing session. This web based payment can be presented in three ways:

1. As a payment menu, displaying all available instruments for the payment
order.
2. As a payment menu, restricting the instruments to a specific subset of the
available instruments for the payment order.
3. As instrument mode, displaying only one specific instrument.

#### Payment menu

For a payment menu with all available instruments for the payment order, we
specify the `mode` to `menu` and `restrictedToInstruments` to an empty value.

```swift
paymentSession.createSwedbankPaySDKController(mode: .menu(restrictedToInstruments: nil))
```

```kotlin
paymentSession.createPaymentFragment(mode = SwedbankPayPaymentSessionSDKControllerMode.Menu(null))
```

#### Payment menu, restricted to instruments

For a payment menu restricted to specific instruments, we specify the `mode` to
`menu` and `restrictedToInstruments` to an array with the instruments that you
want to show in the menu. In the example below, we filter out so that only the
purely web based instruments are shown, but you are free to specify any logic.
You are free to include both instruments that are web based and those that can
be performed as native payments.

```swift
let restrictedToInstruments = availableInstruments.filter {
if case .webBased = $0 {
return true
}

return false
}
paymentSession.createSwedbankPaySDKController(mode: .menu(restrictedToInstruments: restrictedToInstruments))
```


```kotlin
val restrictedToInstruments = availableInstruments.filterIsInstance<AvailableInstrument.WebBased>()
paymentSession.createPaymentFragment(mode = SwedbankPayPaymentSessionSDKControllerMode.Menu(restrictedToInstruments = restrictedToInstruments))
```

#### Instrument mode

For instrument mode, we specify `mode` to `instrumentMode` and `instrument` to
the specific instrument that was picked by the user. Note that the SDK exposes
the instrument `paymentMethod` identifiers as strings, and it's up to you to
identify the relevant instrument from the list of available instruments. In the
example below, we explicitly look for the `Invoice-PayExFinancingSe` instrument.

```swift
if let instrument = availableInstruments.first(where: { $0.paymentMethod == "Invoice-PayExFinancingSe" }) {
paymentSession.createSwedbankPaySDKController(mode: .instrumentMode(instrument: instrument))
}
```

```kotlin
availableInstruments.firstOrNull { it.paymentMethod == "Invoice-PayExFinancingSe" }?.let { instrument ->
paymentSession.createPaymentFragment(SwedbankPayPaymentSessionSDKControllerMode.InstrumentMode(instrument = instrument))
}
```

## Detailed usage flows

To expand on the simple happy flow, there are a few more possible scenarios when
Expand Down Expand Up @@ -943,48 +1057,6 @@ sequenceDiagram
* ④ See [Problem handling][problem-handling] for different considerations and
outcomes.

### Alternative checkout flows

Depending on the checkout flow in your app, there might be a need to implement
the Native Payment feature slightly different.

#### Payment menu fallback

You might want to give the user an option to use the regular, web view based,
payment menu in the SDK. One simple way to achieve this is to reuse the same
payment order and present the regular SDK UI depending on the user choice:

```mermaid
sequenceDiagram
participant App
participant SDK
App ->> SDK: fetchPaymentSession()
activate SDK
App ->> App: Show loading indicator
SDK ->> App: paymentSessionFetched()
deactivate SDK
App ->> App: Present available payment methods to user
note over App: Include custom option<br/>"More payment methods"
opt If a native payment instrument is picked by user
App ->> SDK: makeNativePaymentAttempt()
activate SDK
App ->> App: Show loading indicator
SDK ->> App: paymentComplete()
deactivate SDK
end
opt If "More payment methods" option is picked by user
App ->> SDK: Create payment menu
activate SDK
note over App, SDK: Android: createPaymentFragment()<br/>iOS: createSwedbankPaySDKController()
SDK ->> App: Show payment menu
note over App, SDK: Android: ShowPaymentFragment<br/>iOS: showSwedbankPaySDKController()
App ->> App: Present web based payment menu
SDK ->> App: paymentComplete()
deactivate SDK
end
```

#### Present methods before payment order creation

There might be scenarios where you want to presenting payment methods to your
Expand Down

0 comments on commit c03c88b

Please sign in to comment.