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.

Non-PSP adapter

  1. Our checkout will create an order using the order API.
  2. Our checkout will then create a payment using the Non-psp adapter.
  3. The Non-PSP adapter will fetch its configuration to get the available payment methods.
  4. 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.

Set configuration

Copy
Copied
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.

Create order

Copy
Copied
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.

Create Shipping

Copy
Copied
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.

Create payment

Create Non-psp payment requestCreate Non-psp payment response
Copy
Copied
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
Copy
Copied
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 order

Get order with payment requestGet order with payment response
Copy
Copied
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
Copy
Copied
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.

Update cart

Update cart requestUpdate cart response
Copy
Copied
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
  }
}
Copy
Copied
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 order

Get order with payment requestGet order with payment response
Copy
Copied
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
Copy
Copied
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.

Update payment method

Update payment method requestUpdate payment method response
Copy
Copied
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"
}
Copy
Copied
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.

Complete payment

Copy
Copied
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 order

Get completed order requestGet completed order response
Copy
Copied
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
Copy
Copied
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!

Copyright © Norce 2024. All right reserved.