# Strapi plugin Payments

Enable payments in your Strapi application.

Integrate with any other plugin or custom controller and create orders using a service-oriented approach.

## Features

- Use Stripe and Paypal to process payments.
- Use available services to find, create and confirm orders.
- Configure everything from the settings dashboard.

## [Video tutorial](https://youtu.be/prG1UJHZZQ8)

## Requirements

- Stripe account
  - Secret key
- Paypal merchant app
  - Client ID
  - Client secret

## Installation

In the root of your strapi application, run the following command:

```bash
npm i strapi-plugin-payments
```

## Configuration

Once installed, go to `settings`, then `Payments Plugin` and under `Stripe`, set the Stripe private key and the `success URL` and `cancel url`.

Similarly, in order to accept payments with Paypal, open the `Paypal` tab, set the `Brand Name`, the `PayPal client ID`, the `PayPal client secret` and the `return URL` and `cancel url`.

## Usage

The plugin allows to create, find and confirm orders through a few service APIs.

Each service may return an error if something goes wrong or the plugin is not configured properly.

The returned errors are objects with the following structure:

```ts
{
  error: boolean;
  status: string;
  msg: string;
}
```

The `status` and `msg` can be used to return an HTTP response, for instance, this could be your custom controller:

```js
if (result.error) {
  return ctx[result.status](result.msg)
}
```

### create

For creating orders, call the **asynchronous** service `create`, which receives the following parameters:

| Name | Type | Description |
|---|---|---|
| `user` | `User` from `User & Permissions` plugin |  |
| `payment_method` | string | `"credit_card"` or `"paypal"` |
| `payload` | JSON | This data will be available for you when confirming and finding orders. |
| `items` | Array of objects | Each object in the array must have the propierties: `label` (string) `price` (number) and `quantity` (number). |


#### Example

```js
const params = {
  user: ctx.state.user,
  payment_method: "credit_card",
  payload: {courses_ids: [4]},
  items: [
    {
      label: "Python Essentials",
      price: 15.99,
      quantity: 1
    }
  ]
}

let result

try {
  result = await strapi.service("plugin::payments.orders").create(params)
  if (result.error) {
    return ctx[result.status](result.msg)
  }
} catch(err) {
  console.log(err)
  return ctx.internalServerError("Something went wrong")
}
```



If the order was created successfully, this service returns an object with a few properties that can be used to redirect the user to checkout.

When paying with credit card, the returned object will have the following properties:

| Name | Type |
|---|---|
| `id` | string |

`id` is the order's checkout session, which can be used in the frontend to redirect the user to the checkout page, like the following snippet.

Notice that `stripe.redirectToCheckout` is a function that comes from the package `@stripe/stripejs` for redirecting the user to Stripe's checkout page.

```js
const url = `${STRAPI}/api/orders`

const options = {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${user.token}`,
    "Content-type": "application/json"
  },
  body: JSON.stringify({
    courses: [6,43], // these are courses IDs
    method: "credit_card"
  })
}

const res = await fetch(url, options)
const data = await res.json()

const { id } = data

stripe.redirectToCheckout({
  sessionId: id
})
```

When paying with Paypal, the returned object will have the following properties:

| Name | Type |
|---|---|
| `id` | string |
| `links` | Array |

`id` is the order's ID generated by paypal, and `links` is an array of links that we can use to redirect the user to Paypal's checkout page.

For instance, check the following code snippet that runs in the frontend.

```js
const url = `${STRAPI}/api/orders`

// Paying with paypal.
const options = {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${user.token}`,
    "Content-type": "application/json"
  },
  body: JSON.stringify({
    courses: [6,43], // these are courses IDs
    method: "paypal"
  })
}

const res = await fetch(url, options)
const data = await res.json()

const { links } = data
const link = links.find(l => l.rel === "approve")

// Redirect to checkout.
document.location = link.href
```

### confirm

This **asynchronous** service sends a request to either Paypal or Stripe to confirm a payment.

It receives the following parameters:

| Name | Type | Description |
|---|---|---|
| `user` | `User` from `User & Permissions` plugin | |
| `checkout_session` | string | the token in the success url page |

When confirming a payment, the user in the parameter must be the same as the user which the order is related to.

If the payment is confirmed, this service returns the order object with a field `confirmed` set to true.

Otherwise, the service returns an error object with the same properties as the `create` service.

`Order` object structure:

| Property | Type |
|---|---|
| `id` | string |
| `amount` | number |
| `createdAt` | Date |
| `user` | Object with `id` as the only property |
| `confirmed` | boolean |
| `checkout_session` | string |
| `payment_method` | `"credit_card"` or `"paypal"` |
| `payload` | JSON |
| `items` | JSON |
| `response` | JSON representing additional information |

### find

Finding the orders of a user is done through the `find` service.

It receives a `user` and returns an array containing all the orders of the user.

For instance, this could be your custom controller to return the user's orders.

```js
const { user } = ctx.state

const orders = await strapi.service("plugin::payments.orders").find(user)

ctx.body = {
  orders
}
```

### findOne

This service is for searching a specific order by checkout session, taking two parameters: the `user` and the order's `checkout session`.

See the following example of a custom controller invoking this service.

```js
const { user } = ctx.state
const { id } = ctx.params;

const order = await strapi.service("plugin::payments.orders").findOne(user)

ctx.body = {
  order
}
```

In case the order is not found, the return value will be null.

The user provided as a parameter must be equal to the user which the order belongs to. Otherwise, a `forbidden` error is returned.