Vulcan/packages/vulcan-payments/README.md

193 lines
6.6 KiB
Markdown
Raw Normal View History

2017-05-31 10:25:13 +09:00
# Vulcan Payments
This package helps you process charges with Vulcan. It currently only supports Stripe, but other payment processors may be supported in the future (PRs welcome!).
2017-05-31 10:32:35 +09:00
## Overview
2017-05-31 10:25:13 +09:00
This package does the following things:
- Provide a button that triggers the [Stripe Checkout](https://stripe.com/checkout) form.
2017-05-31 10:32:35 +09:00
- Once the form is submitted, trigger a GraphQL mutation that will:
- Perform the charge.
- Create a new Charge.
- Modify a document associated with the charge.
- The mutation then returns a document associated with the charge to the client.
2017-05-31 10:25:13 +09:00
2017-05-31 10:32:35 +09:00
## Settings
2017-05-31 10:25:13 +09:00
Stripe requires the following public setting in your `settings.json`.
- `public.stripe.publishableKey`: your publishable Stripe key.
As well as the following private setting (can be stored in the setting's root or on `private`):
- `stripe.secretKey`: your Stripe secret key.
```
{
"public": {
"stripe": {
"publishableKey": "pk_test_K0rkFDrT0jj4NqG5Dumr3RaU"
}
}
},
"stripe": {
"secretKey": "sk_test_sfdhj34jdsfxhjs234sd0K",
"createPlans": false
2017-05-31 10:25:13 +09:00
},
}
```
2017-05-31 10:32:35 +09:00
## Charges
Charges are stored in the database with the following fields:
- `_id`: the charge's id.
- `createdAt`: the charge's timestamp.
- `userId`: the Vulcan `_id` of the user performing the purchase.
- `tokenId`: the charge token's id.
- `productKey`: the key corresponding to the product being purchased, as defined with `addProduct`.
- `type`: the type of charge (currently only `stripe` is supported).
- `test`: whether the operation is a test or not.
- `data`: a JSON object containing all charge data generated by the payment processor.
- `properties`: a JSON object containing any custom properties passed by the client.
- `ip`: the IP address of the client performing the purchase.
## Products
2017-05-31 10:25:13 +09:00
A product is a type of purchase a user can make. It has a `name`, `amount` (in cents), `currency`, and `description`.
New products are defined using the `addProduct` function, which takes two arguments. The first argument is a unique **product key** used to identify the product. The second argument can be an object (for "static" products like a subscription):
```
2017-05-31 10:27:32 +09:00
import { addProduct } from 'meteor/vulcan:payments';
2017-05-31 10:25:13 +09:00
addProduct('membership', {
'amount': 25000,
'currency': 'USD',
'description': 'Become a paid member.'
});
```
Or it can be a function (for "dynamic" products like in an e-commerce site) that takes the associated document (i.e. the product being sold) as argument and returns an object:
```
2017-05-31 10:27:32 +09:00
import { addProduct } from 'meteor/vulcan:payments';
2017-05-31 10:25:13 +09:00
addProduct('book', book => ({
'name': book.title,
'amount': book.price,
'currency': 'USD',
'description': book.description
}));
```
Make sure you define your products in a location accessible to both client and server, in order to access them both on the front-end to configure Stripe Checkout, and in the back-end to perform the actual charge.
2017-05-31 10:32:35 +09:00
## Checkout Component
2017-05-31 10:25:13 +09:00
```js
<Components.Checkout
productKey="jobPosting"
associatedCollection={Jobs}
associatedId={job._id}
callback={setToPaid}
button={<Button className="buy-job-button" bsStyle="primary">Complete Payment</Button>}
/>
```
- `productKey`: The key of the product to buy.
- `button`: The button that triggers the Stripe Checkout overlay.
- `associatedCollection`: the associated collection.
- `associatedDocument`: the associated `document`.
- `callback`: a callback function that runs once the charge is successful (takes the `charge` as result argument).
- `fragment`: a GraphQL fragment specifying the fields expected in return after the charge.
- `fragmentName`: a registeredGraphQL fragment name.
- `properties`: any other properties you want to pass on to `createChargeMutation` on the server.
2017-05-31 10:32:35 +09:00
## Associating a Collection Document
2017-05-31 10:25:13 +09:00
The Vulcan Charge package requires associating a document with a purchase, typically the item being paid for. For example, maybe you want people to buy access to a file hosted on your servers, and give them download access once the transaction is complete.
2017-05-31 10:32:35 +09:00
The `associatedCollection` and `associatedId` props give you an easy way to implement this by automatically setting a `chargeIds` field on the document once the charge succeeds.
2017-05-31 10:25:13 +09:00
For example, if you pass `associatedCollection={Jobs}` and `associatedId="foo123"` to the Checkout component, the resulting charge's `_id` will automatically be added to a `chargeIds` array on job `foo123`.
The `createChargeMutation` GraphQL mutation will then return that job according to the `fragment` property specified.
2017-05-31 10:32:35 +09:00
Note: you will need to make sure that your collection accepts this `chargeIds` field. For example:
```js
Jobs.addField([
{
fieldName: 'chargeIds',
fieldSchema: {
type: Array,
optional: true,
}
},
{
fieldName: 'chargeIds.$',
fieldSchema: {
type: String,
optional: true,
}
}
]);
```
#### The "Chargeable" Type
2017-05-31 10:25:13 +09:00
2017-05-31 10:32:35 +09:00
In order to be able to return any associated document, the package creates a new `Chargeable` GraphQL type that is an union of every collection's types.
2017-05-31 10:25:13 +09:00
2017-05-31 10:32:35 +09:00
## Post-Charge Updates
2017-05-31 10:25:13 +09:00
The best way to update a document based on a successful charge is by using the `collection.charge.sync` callback.
Callback functions on this hook will run with a MongoDB `modifier` as the first argument (although note that only `$set` and `$unset` operations are supported here), the `document` associated with the charge as their second argument, and the `charge` object as their third argument.
Because the callback is added in a **sync** manner, the final document returned by the `createChargeMutation` mutation will include any new values set by the callback hook.
#### Example 1: Setting a job offer as paid
```js
import { addCallback } from 'meteor/vulcan:core';
function setToPaidOnCharge (modifier, job, charge) {
modifier.$set.status = 'paid';
return modifier;
}
addCallback('jobs.charge.sync', setToPaidOnCharge);
```
#### Example 2: Adding a user to a group
```js
import { addCallback } from 'meteor/vulcan:core';
function makePaidMember (modifier, user, charge) {
modifier.$set.groups = [...user.groups, 'paidMembers'];
return modifier;
}
addCallback('users.charge.sync', makePaidMember);
```
#### Example 3: Giving a user access to a specific document
We'll pass the `videoId` property to our `Checkout` component (`property={{videoId: video._id}}`) to make it accessible as `charge.properties.videoId` inside the callback:
```js
import { addCallback } from 'meteor/vulcan:core';
function giveAccessToVideo (modifier, user, charge) {
const videoId = charge.properties.videoId;
modifier.$set.accessibleVideos = [...user.accessibleVideos, videoId];
return modifier;
}
addCallback('users.charge.sync', giveAccessToVideo);
```