Using our first adapter
In this tutorial we'll use a similar case to the last one, but we'll offer two payment methods. To make handling payment methods easier we'll use our first payment adapter.
The Non-PSP adapter allows us to configure some payment methods to be made available and to mark one of them as being pre-selected.
Below we can se a quick overview of how it will work.
- Our checkout will create an order using the order API.
- Our checkout will then create a payment using the Non-psp adapter.
- The Non-PSP adapter will fetch its configuration to get the available payment methods.
- The Non-PSP will fetch the order, find the total of the order for it to cover in the payment. It will then create the payment on our order, as well as add some hooks .
Configure Non-PSP adapter
The Non-PSP adapter provides a schema for it's configuration. It's a pretty basic configuration, we need to provide some urls to the adapter and we can configure our payment methods and we can make one of them be pre selected.
Let's assume we now provide these payment methods.
Payment Method ID | Name |
---|---|
thing-payment | Thing Payment |
pay-stuff | Pay Stuff |
We then provide a configuration as show below.
PUT /api/v1/configuration/merchants/thingsandstuff/channels/se/configurations/nonpsp_adapter
X-Merchant: thingsandstuff
X-Channel: se
Authorization: Bearer super-secret-and-valid-token
Host: thingsandstuff.api-se.playground.norce.tech/checkout/configuration
Content-Type: application/json
{
"$schema": "https://thingsandstuff.api-se.playground.norce.tech/checkout/nonpsp-adapter/openapi/v1/schemas/nonpsp_adapter.json",
"id": "nonpsp_adapter",
"active": true,
"adapter": {
"internalUrl": "https://nonpsp-adapter.checkout.playground.internal.norce.tech",
"publicUrl": "https://thingsandstuff.api-se.playground.norce.tech/checkout/nonpsp-adapter"
},
"methods": [
{
"identifier": "thing-payment",
"preSelected": true
},
{
"identifier": "pay-stuff",
"preSelected": false
}
]
}
info
Configuration can be done a lot easier through the Norce Checkout admin. But using the API is easier for sharing these examples. It's also neat to see how it works behind the scene!
Create an order
This time when we create an order we'll include our customer information directly.
POST /api/v0/checkout/orders
X-Merchant: thingsandstuff
X-Channel: se
Authorization: Bearer super-secret-and-valid-token
Host: thingsandstuff.api-se.playground.norce.tech/checkout/order
Content-Type: application/json
{
"merchant": "thingsandstuff",
"channel": "se",
"cart": {
"reference": "our-second-cart",
"items": [
{
"reference": "cart-item-no-2",
"sku": "S001",
"name": "Stuff",
"quantity": 1,
"total": {
"includingVat": 12.5,
"excludingVat": 10
},
"vatRate": 0.25
}
],
"total": {
"includingVat": 12.5,
"excludingVat": 10
}
},
"customer": {
"billing": {
"type": "Person",
"givenName": "Henrikinho",
"familyName": "Undulatsson",
"streetAddress": "Odengatanvägen 12",
"streetAddress2": "Lgh 1203",
"postalCode": "915 20",
"city": "Skellefteå",
"country": "SE",
"phone": "0701740612",
"email": "henrikinho.undulatsson@example.com"
},
"shipping": {
"type": "Person",
"givenName": "Henrikinho",
"familyName": "Undulatsson",
"streetAddress": "Odengatanvägen 12",
"streetAddress2": "Lgh 1203",
"postalCode": "915 20",
"city": "Skellefteå",
"country": "SE",
"phone": "0701740612",
"email": "henrikinho.undulatsson@example.com"
}
}
}
Delivery
Next we'll create our shipping, just like we did in the previous tutorial.
POST /api/v0/checkout/orders/oaAaAaAA/shippings
X-Merchant: thingsandstuff
X-Channel: se
Authorization: Bearer super-secret-and-valid-token
Host: thingsandstuff.api-se.playground.norce.tech/checkout/order
Content-Type: application/json
{
"reference": "thing-to-home-delivery",
"name": "Thing to Home Delivery",
"total": {
"includingVat": 7.5,
"excludingVat": 6
},
"vatRate": 0.25
}
Payment
This time instead of creating the payment through the order API we instead use the Non-PSP adapter API. We don't even need to calculate the amount to cover.
In the response we now get all available payment methods as well as the selected payment method if we wish to display that in our checkout.
POST /api/v1/checkout/orders/oaAaAaAA/payments
X-Merchant: thingsandstuff
X-Channel: se
Authorization: Bearer super-secret-and-valid-token
Host: thingsandstuff.api-se.playground.norce.tech/checkout/nonpsp-adapter
201 Created
location: /api/v0/checkout/orders/oaAaAaAA/payments/paAaaAAAAaaaaAAAAaAAAAAAaaA
Content-Type: application/json
{
"paymentId": "paAaaAAAAaaaaAAAAaAAAAAAaaA",
"paymentMethods": [
{
"identifier": "thing-payment"
},
{
"identifier": "pay-stuff"
}
],
"selectedPaymentMethod": "thing-payment"
}
Fetch the order again and we'll see that a payment has been created on the order, using our pre selected method, and the amount covers the total of the order.
Another thing we can notice are the hooks added to the order. One that subscribes to /cart
and one that subscribes to /shippings
both targeting /payments
. This means that any time we make any changes to either /cart
or /shippings
the Non-PSP adapter will be invoked with the changes and able to make additional changes to its payment. All before the original change is committed.
GET /api/v0/checkout/orders/oaAaAaAA
X-Merchant: thingsandstuff
X-Channel: se
Authorization: Bearer super-secret-and-valid-token
Host: thingsandstuff.api-se.playground.norce.tech/checkout/order
200 OK
Content-Type: application/json
{
"id": "oaAaAaAA",
"merchant": "thingsandstuff",
"channel": "se",
"created": "2024-01-01T01:01:01.0000001Z",
"lastModified": "2024-01-01T01:04:01.0000001Z",
"state": {
"currentStatus": "checkout",
"transitions": [
{
"status": "checkout",
"timeStamp": "2024-01-01T01:01:01.0000001Z",
}
]
},
"cart": {
"reference": "our-second-cart",
"items": [
{
"id": "ciAaaAAAAaaaaAAAAaAAAAAAaaA",
"reference": "cart-item-no-2",
"sku": "S001",
"name": "Stuff",
"quantity": 1,
"total": {
"includingVat": 12.5,
"excludingVat": 10
},
"vatRate": 0.25
}
],
"total": {
"includingVat": 12.5,
"excludingVat": 10
}
},
"shippings": [
{
"id": "saAaaAAAAaaaaAAAAaAAAAAAaaA",
"state": "intent",
"reference": "thing-to-home-delivery",
"name": "Thing to Home Delivery",
"total": {
"includingVat": 7.5,
"excludingVat": 6
},
"vatRate": 0.25
}
],
"payments": [
{
"id": "paAaaAAAAaaaaAAAAaAAAAAAaaA",
"name": "Thing Payment",
"currency": "SEK",
"type": "default",
"orderId": "oaAaAaAA",
"amount": 20,
"state": "intent",
"reference": "thing-payment"
}
],
"hooks": [
{
"id": "ohaAaaAAAAaaaaAAAAaAAAAAAaaA",
"adapterId": "nonpsp_adapter",
"subscribeTo": "/cart",
"target": "/payments",
"invoke": "https://.../cart-changed"
},
{
"id": "ohaAaaAAAAaaaaAAAAaAAAAAAaaA",
"adapterId": "nonpsp_adapter",
"subscribeTo": "/shippings",
"target": "/payments",
"invoke": "https://.../shippings-changed"
}
],
"total": {
"includingVat": 20,
"excludingVat": 16
}
}
Updating quantity
Let's try out the cart hook by updating the quantity of an item in the cart. Don't forget to calculate the new cart total.
PUT /api/v0/checkout/orders/oaAaAaAA/cart
X-Merchant: thingsandstuff
X-Channel: se
Authorization: Bearer super-secret-and-valid-token
Host: thingsandstuff.api-se.playground.norce.tech/checkout/order
Content-Type: application/json
{
"items": [
{
"id": "ciAaaAAAAaaaaAAAAaAAAAAAaaA",
"reference": "cart-item-no-2",
"sku": "S001",
"name": "Stuff",
"quantity": 2,
"total": {
"includingVat": 25,
"excludingVat": 20
},
"vatRate": 0.25
}
],
"total": {
"includingVat": 25,
"excludingVat": 20
}
}
200 OK
If we now fetch the order again we should see that our payment amount has increased to cover the new order total.
GET /api/v0/checkout/orders/oaAaAaAA
X-Merchant: thingsandstuff
X-Channel: se
Authorization: Bearer super-secret-and-valid-token
Host: thingsandstuff.api-se.playground.norce.tech/checkout/order
200 OK
Content-Type: application/json
{
"id": "oaAaAaAA",
"merchant": "thingsandstuff",
"channel": "se",
"created": "2024-01-01T01:01:01.0000001Z",
"lastModified": "2024-01-01T01:04:01.0000001Z",
"state": {
"currentStatus": "checkout",
"transitions": [
{
"status": "checkout",
"timeStamp": "2024-01-01T01:01:01.0000001Z",
}
]
},
"cart": {
"reference": "our-second-cart",
"items": [
{
"id": "ciAaaAAAAaaaaAAAAaAAAAAAaaA",
"reference": "cart-item-no-2",
"sku": "S001",
"name": "Stuff",
"quantity": 2,
"total": {
"includingVat": 12.5,
"excludingVat": 10
},
"vatRate": 0.25
}
],
"total": {
"includingVat": 25,
"excludingVat": 20
}
},
"shippings": [
{
"id": "saAaaAAAAaaaaAAAAaAAAAAAaaA",
"state": "intent",
"reference": "thing-to-home-delivery",
"name": "Thing to Home Delivery",
"total": {
"includingVat": 7.5,
"excludingVat": 6
},
"vatRate": 0.25
}
],
"payments": [
{
"id": "paAaaAAAAaaaaAAAAaAAAAAAaaA",
"name": "Thing Payment",
"currency": "SEK",
"type": "default",
"orderId": "oaAaAaAA",
"amount": 32.5,
"state": "intent",
"reference": "thing-payment"
}
],
"hooks": [
{
"id": "ohaAaaAAAAaaaaAAAAaAAAAAAaaA",
"adapterId": "nonpsp_adapter",
"subscribeTo": "/cart",
"target": "/payments",
"invoke": "https://.../cart-changed"
},
{
"id": "ohaAaaAAAAaaaaAAAAaAAAAAAaaA",
"adapterId": "nonpsp_adapter",
"subscribeTo": "/shippings",
"target": "/payments",
"invoke": "https://.../shippings-changed"
}
],
"total": {
"includingVat": 32.5,
"excludingVat": 26
}
}
Change payment method
To change the selected payment method using the Non-PSP adapter we simply update the payment through the Non-PSP adapter providing our new selected payment method. Our response will look similar to when we created the payment, except that our selected payment method will be updated.
PUT /api/v1/checkout/orders/oaAaAaAA/payments
X-Merchant: thingsandstuff
X-Channel: se
Authorization: Bearer super-secret-and-valid-token
Host: thingsandstuff.api-se.playground.norce.tech/checkout/nonpsp-adapter
Content-Type: application/json
{
"selectedPaymentMethod": "pay-stuff"
}
200 OK
Content-Type: application/json
{
"paymentId": "paAaaAAAAaaaaAAAAaAAAAAAaaA",
"paymentMethods": [
{
"identifier": "thing-payment"
},
{
"identifier": "pay-stuff"
}
],
"selectedPaymentMethod": "pay-stuff"
}
Complete the order
Instead of completing the order directly we can have our adapter do it by completing the payment.
POST /api/v1/checkout/orders/oaAaAaAA/payments/pAAAAAAAA/complete
X-Merchant: thingsandstuff
X-Channel: se
Authorization: Bearer super-secret-and-valid-token
Host: thingsandstuff.api-se.playground.norce.tech/checkout/nonpsp-adapter
Content-Type: application/json
Fetching our order for the final time we can see that the order has been completed again but this time by the adapter.
GET /api/v0/checkout/orders/oaAaAaAA
X-Merchant: thingsandstuff
X-Channel: se
Authorization: Bearer super-secret-and-valid-token
Host: thingsandstuff.api-se.playground.norce.tech/checkout/order
200 OK
Content-Type: application/json
{
"id": "oaAaAaAA",
"merchant": "thingsandstuff",
"channel": "se",
"created": "2024-01-01T01:01:01.0000001Z",
"lastModified": "2024-01-01T01:04:01.0000001Z",
"state": {
"currentStatus": "checkout",
"transitions": [
{
"status": "checkout",
"timeStamp": "2024-01-01T01:01:01.0000001Z",
},
{
"status": "processing",
"timeStamp": "2024-01-01T01:04:01.0000001Z",
},
{
"status": "accepted",
"timeStamp": "2024-01-01T01:04:01.0000001Z",
},
{
"status": "completed",
"timeStamp": "2024-01-01T01:04:01.0000001Z",
}
]
},
"cart": {
"reference": "our-second-cart",
"items": [
{
"id": "ciAaaAAAAaaaaAAAAaAAAAAAaaA",
"reference": "cart-item-no-2",
"sku": "S001",
"name": "Stuff",
"quantity": 2,
"total": {
"includingVat": 12.5,
"excludingVat": 10
},
"vatRate": 0.25
}
],
"total": {
"includingVat": 25,
"excludingVat": 20
}
},
"shippings": [
{
"id": "saAaaAAAAaaaaAAAAaAAAAAAaaA",
"state": "intent",
"reference": "thing-to-home-delivery",
"name": "Thing to Home Delivery",
"total": {
"includingVat": 7.5,
"excludingVat": 6
},
"vatRate": 0.25
}
],
"payments": [
{
"id": "paAaaAAAAaaaaAAAAaAAAAAAaaA",
"name": "Thing Payment",
"currency": "SEK",
"type": "default",
"orderId": "oaAaAaAA",
"amount": 32.5,
"state": "intent",
"reference": "thing-payment"
}
],
"hooks": [
{
"id": "ohaAaaAAAAaaaaAAAAaAAAAAAaaA",
"adapterId": "nonpsp_adapter",
"subscribeTo": "/cart",
"target": "/payments",
"invoke": "https://.../cart-changed"
},
{
"id": "ohaAaaAAAAaaaaAAAAaAAAAAAaaA",
"adapterId": "nonpsp_adapter",
"subscribeTo": "/shippings",
"target": "/payments",
"invoke": "https://.../shippings-changed"
}
],
"total": {
"includingVat": 32.5,
"excludingVat": 26
}
}
Next steps
Here we worked with our first adapter, but we're still doing a lot of platform things manually. Let's start Norce Commerce and the Norce Adapter next!