Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature(payments-plugin): PayPal two-step flow implementation #3099

Open
wants to merge 28 commits into
base: minor
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
7392250
feat(payments-plugin): Added shop api extensions for PayPal orders
sonntag-philipp Sep 27, 2024
f72e83e
feat(payments-plugin): Added dev server with ui for payment authoriza…
sonntag-philipp Sep 28, 2024
dd7954e
feat(payments-plugin): Added services for PayPal authorization and or…
sonntag-philipp Oct 1, 2024
bacdc84
feat(payments-plugin): Added methods to handler to handle paypal paym…
sonntag-philipp Oct 1, 2024
5531e36
feat(payments-plugin): Implemented refund logic and types for captures
sonntag-philipp Oct 1, 2024
b1503ed
feat(payments-plugin): Added msw to mock http requests that are made …
sonntag-philipp Oct 1, 2024
dbc8b71
feat(payments-plugin): Added launch script for e2e tests
sonntag-philipp Oct 1, 2024
a81a905
test(payments-plugin): Added e2e tests for payment and order creation…
sonntag-philipp Oct 1, 2024
6ded7b0
feat(payments-plugin): Added e2e tests for all steps necessary for ba…
sonntag-philipp Oct 3, 2024
d165f7d
feat(payments-plugin): Switched to nock
sonntag-philipp Oct 8, 2024
c02e8b2
feat(payments-plugin): Adding e2e tests for paypal refunds
sonntag-philipp Oct 8, 2024
43ea8a5
feat(payments-plugin): Added remaining e2e tests for refunds
sonntag-philipp Oct 10, 2024
97acd48
feat(payments-plugin): Added channel specific tests for order creation
sonntag-philipp Oct 10, 2024
2573d56
feat(payments-plugin): Removed msw dependency
sonntag-philipp Oct 10, 2024
eb073d2
feat(payments-plugin): Added better error logging for PayPal requests
sonntag-philipp Oct 10, 2024
349dbcd
feat(payments-plugin): Added env variable for PayPal base uri
sonntag-philipp Oct 14, 2024
ab3f6e5
chore: Updated the package-lock
sonntag-philipp Oct 14, 2024
a35c1d4
refactor(payments-plugin): Added translations to PayPal errors
sonntag-philipp Oct 14, 2024
ed3cef0
fix(payments-plugin): Fixed issue with paypal-sdk plugin of dev server
sonntag-philipp Oct 14, 2024
50140ba
fix(payments-plugin): Fixed PayPal e2e tests and updated translations
sonntag-philipp Oct 16, 2024
7886553
chore: Updated package lockfile
sonntag-philipp Oct 16, 2024
a5a978a
test(payments-plugin): Fixed e2e tests of the payments plugin
sonntag-philipp Oct 18, 2024
4c704a2
fix(payments-plugin): Fixed stripe tests
sonntag-philipp Oct 19, 2024
5b02301
test(payments-plugin): Modified test cases so array order is irreleva…
sonntag-philipp Oct 19, 2024
bbd8317
docs(payments-plugin): Added initial documentation to PayPalPlugin
sonntag-philipp Nov 14, 2024
82db1a8
docs: Incremented theme search typesense version and upgraded remaini…
sonntag-philipp Nov 14, 2024
d390175
docs(payments-plugin): Added documentation to PayPal plugin
sonntag-philipp Nov 15, 2024
05251c8
docs(payments-plugin): Smaller fixes for the PayPal plugin documentation
sonntag-philipp Nov 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,20 @@
"runtimeArgs": ["--transpile-only"],
"skipFiles": ["<node_internals>/**", "node_modules/**"],
"cwd": "${workspaceRoot}",
"resolveSourceMapLocations": ["${workspaceFolder}/**", "!**/node_modules/**"],
"resolveSourceMapLocations": ["${workspaceFolder}/**", "!**/node_modules/**"]
},
{
"name": "Debug e2e",
"type": "node",
"request": "launch",
"autoAttachChildProcesses": true,
"skipFiles": ["<node_internals>/**", "**/node_modules/**"],
"program": "${workspaceRoot}/node_modules/vitest/vitest.mjs",
"env": {
"TEST_FILE": "${relativeFile}"
},
"args": ["--config", "${workspaceRoot}/e2e-common/vitest.config.mts", "watch", "${relativeFile}"],
"smartStep": true,
"console": "integratedTerminal"
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';

## ElasticsearchPlugin

<GenerationInfo sourceFile="packages/elasticsearch-plugin/src/plugin.ts" sourceLine="223" packageName="@vendure/elasticsearch-plugin" />
<GenerationInfo sourceFile="packages/elasticsearch-plugin/src/plugin.ts" sourceLine="224" packageName="@vendure/elasticsearch-plugin" />

This plugin allows your product search to be powered by [Elasticsearch](https://github.com/elastic/elasticsearch) - a powerful Open Source search
engine. This is a drop-in replacement for the DefaultSearchPlugin which exposes many powerful configuration options enabling your storefront
Expand Down
143 changes: 143 additions & 0 deletions docs/docs/reference/core-plugins/payments-plugin/pay-pal-plugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
---
title: "PayPalPlugin"
isDefaultIndex: false
generated: true
---
<!-- This file was generated from the Vendure source. Do not modify. Instead, re-run the "docs:build" script -->
import MemberInfo from '@site/src/components/MemberInfo';
import GenerationInfo from '@site/src/components/GenerationInfo';
import MemberDescription from '@site/src/components/MemberDescription';


## PayPalPlugin

<GenerationInfo sourceFile="packages/payments-plugin/src/paypal/paypal.plugin.ts" sourceLine="124" packageName="@vendure/payments-plugin" />

This plugin enables payments via the [PayPal Order API](https://developer.paypal.com/docs/api/orders/v2/#orders_create).

> **_Note:_** This plugin only supports a [2 step flow](https://docs.vendure.io/guides/core-concepts/payment/#two-step).
> This means that the payment has to manually captured by an admin.

## Requirements

1. Create a PayPal business account.
2. Make sure you have the client ID and secret of your PayPal REST API app.
3. Get the merchant ID from your PayPal account. This is the account ID of your PayPal business account.
4. Install the Payments plugin:

`yarn add @vendure/payments-plugin`

or

`npm install @vendure/payments-plugin`

## Setup

1. Add the plugin to your VendureConfig `plugins` array:
```ts
import { PayPalPlugin } from '@vendure/payments-plugin/package/paypal';

// ...

plugins: [
// Set the apiUrl to the PayPal sandbox environment
PayPalPlugin.init({ apiUrl: 'https://sandbox.paypal.com/' }),
]
```
2. Create a new PaymentMethod in the Admin UI, and select "PayPal payments" as the handler.
3. Set your PayPal client ID, secret and merchant ID in the according fields.

## Storefront usage

To successfully make a payment, the following steps are explained in detail:
1. Create a PayPal order
2. Use the PayPal SDK to authorize the payment
3. Add the payment to the order
4. Capture the payment

### Create PayPal order

Begin by creating a new PayPal order. This order is the reference for the payment and is used to authorize the payment.
Make sure to store the order ID in your frontend, as it is needed in the next steps.

This step does not modify any data on your Vendure instance. It only creates a new order in the PayPal system.

Create the PayPal order using the following mutation:
```GraphQL
mutation CreatePayPalOrder {
createPayPalOrder() {
... on PayPalOrder {
id
}
}
}
```

### Authorize payment
The PayPal order you created must be authorized by your customers. This step is handled by the PayPal SDK for the most part.

For JavaScript projects, you can check out this integration guide to
integrate the PayPal SDK: [PayPal SDK Integration Guide](https://developer.paypal.com/studio/checkout/standard/integrate).

### Add payment
After authorizing the payment, you need to add it to the Vendure order. This will add and validate the authorizations
add to the payment in the previous step. If the payment is valid, the order will be updated to the next state.

```GraphQL
mutation AddPaymentToOrder() {
addPaymentToOrder(input: {
method: "paypal-payment-method",
metadata: {
orderId: "the PayPal order ID"
}
}) {
... on Order {
id
code
state
payments {
id
state
transactionId
method
}
}
... on ErrorResult {
message
errorCode
}
... on PaymentFailedError {
paymentErrorMessage
}
}
}
```

### Capture payment
Using the admin ui, the admin can settle this payment. After this step, the payment is successfully captured.

## Creating refunds
Refunds can be created like any other refund in Vendure. The refund will be processed through the PayPal API.

```ts title="Signature"
class PayPalPlugin {
static options: PayPalPluginOptions;
init(options: PayPalPluginOptions) => Type<PayPalPlugin>;
}
```

<div className="members-wrapper">

### options

<MemberInfo kind="property" type={`PayPalPluginOptions`} />


### init

<MemberInfo kind="method" type={`(options: PayPalPluginOptions) => Type&#60;<a href='/reference/core-plugins/payments-plugin/pay-pal-plugin#paypalplugin'>PayPalPlugin</a>&#62;`} />

Initialize the PayPal payment plugin


</div>
24 changes: 24 additions & 0 deletions docs/docs/reference/graphql-api/admin/mutations.md
Original file line number Diff line number Diff line change
Expand Up @@ -1538,6 +1538,30 @@ import MemberDescription from '@site/src/components/MemberDescription';
<div class="graphql-code-line ">transitionPaymentToState(id: <a href="/reference/graphql-api/admin/object-types#id">ID</a>!, state: <a href="/reference/graphql-api/admin/object-types#string">String</a>!): <a href="/reference/graphql-api/admin/object-types#transitionpaymenttostateresult">TransitionPaymentToStateResult</a>!</div>


<div class="graphql-code-line top-level">&#125;</div>
</div>

## unsetDraftOrderBillingAddress
<div class="graphql-code-block">
<div class="graphql-code-line top-level comment">"""</div>
<div class="graphql-code-line top-level comment">Unsets the billing address for a draft Order</div>
<div class="graphql-code-line top-level comment">"""</div>
<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">Mutation</span> &#123;</div>
<div class="graphql-code-line ">unsetDraftOrderBillingAddress(orderId: <a href="/reference/graphql-api/admin/object-types#id">ID</a>!): <a href="/reference/graphql-api/admin/object-types#order">Order</a>!</div>


<div class="graphql-code-line top-level">&#125;</div>
</div>

## unsetDraftOrderShippingAddress
<div class="graphql-code-block">
<div class="graphql-code-line top-level comment">"""</div>
<div class="graphql-code-line top-level comment">Unsets the sthipping address for a draft Order</div>
<div class="graphql-code-line top-level comment">"""</div>
<div class="graphql-code-line top-level">type <span class="graphql-code-identifier">Mutation</span> &#123;</div>
<div class="graphql-code-line ">unsetDraftOrderShippingAddress(orderId: <a href="/reference/graphql-api/admin/object-types#id">ID</a>!): <a href="/reference/graphql-api/admin/object-types#order">Order</a>!</div>


<div class="graphql-code-line top-level">&#125;</div>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';

## DefaultSearchPlugin

<GenerationInfo sourceFile="packages/core/src/plugin/default-search-plugin/default-search-plugin.ts" sourceLine="69" packageName="@vendure/core" />
<GenerationInfo sourceFile="packages/core/src/plugin/default-search-plugin/default-search-plugin.ts" sourceLine="70" packageName="@vendure/core" />

The DefaultSearchPlugin provides a full-text Product search based on the full-text searching capabilities of the
underlying database.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
title: "AddressBasedTaxZoneStrategy"
isDefaultIndex: false
generated: true
---
<!-- This file was generated from the Vendure source. Do not modify. Instead, re-run the "docs:build" script -->
import MemberInfo from '@site/src/components/MemberInfo';
import GenerationInfo from '@site/src/components/GenerationInfo';
import MemberDescription from '@site/src/components/MemberDescription';


## AddressBasedTaxZoneStrategy

<GenerationInfo sourceFile="packages/core/src/config/tax/address-based-tax-zone-strategy.ts" sourceLine="27" packageName="@vendure/core" since="3.1.0

:::info

This is configured via `taxOptions.taxZoneStrategy = new AddressBasedTaxZoneStrategy()` in
your VendureConfig.

:::" />

Address based <a href='/reference/typescript-api/tax/tax-zone-strategy#taxzonestrategy'>TaxZoneStrategy</a> which tries to find the applicable <a href='/reference/typescript-api/entities/zone#zone'>Zone</a> based on the
country of the billing address, or else the country of the shipping address of the Order.

Returns the default <a href='/reference/typescript-api/entities/channel#channel'>Channel</a>'s default tax zone if no applicable zone is found.

```ts title="Signature"
class AddressBasedTaxZoneStrategy implements TaxZoneStrategy {
determineTaxZone(ctx: RequestContext, zones: Zone[], channel: Channel, order?: Order) => Zone;
}
```
* Implements: <code><a href='/reference/typescript-api/tax/tax-zone-strategy#taxzonestrategy'>TaxZoneStrategy</a></code>



<div className="members-wrapper">

### determineTaxZone

<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, zones: <a href='/reference/typescript-api/entities/zone#zone'>Zone</a>[], channel: <a href='/reference/typescript-api/entities/channel#channel'>Channel</a>, order?: <a href='/reference/typescript-api/entities/order#order'>Order</a>) => <a href='/reference/typescript-api/entities/zone#zone'>Zone</a>`} />




</div>
Loading
Loading