hotfix(paypal): correct payment retrieval on refunds
This commit is contained in:
@@ -26,12 +26,13 @@
|
||||
"cross-env": "^5.2.1",
|
||||
"eslint": "^6.8.0",
|
||||
"jest": "^25.5.2",
|
||||
"medusa-interfaces": "^1.1.10",
|
||||
"medusa-test-utils": "^1.1.12"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "babel src -d . --ignore **/__tests__",
|
||||
"build": "babel src -d . --ignore **/__tests__,**/__mocks__",
|
||||
"prepare": "cross-env NODE_ENV=production npm run build",
|
||||
"watch": "babel -w src --out-dir . --ignore **/__tests__",
|
||||
"watch": "babel -w src --out-dir . --ignore **/__tests__,**/__mocks__",
|
||||
"test": "jest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
export const PayPalClientMock = {
|
||||
execute: jest.fn().mockImplementation((r) => {
|
||||
return {
|
||||
result: r.result,
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
export const PayPalMock = {
|
||||
core: {
|
||||
SandboxEnvironment: function () {
|
||||
this.env = {
|
||||
sandbox: true,
|
||||
live: false,
|
||||
}
|
||||
},
|
||||
LiveEnvironment: function () {
|
||||
this.env = {
|
||||
sandbox: false,
|
||||
live: true,
|
||||
}
|
||||
},
|
||||
PayPalHttpClient: function () {
|
||||
return PayPalClientMock
|
||||
},
|
||||
},
|
||||
|
||||
payments: {
|
||||
AuthorizationsGetRequest: jest.fn().mockImplementation(() => {}),
|
||||
AuthorizationsVoidRequest: jest.fn().mockImplementation(() => {}),
|
||||
AuthorizationsCaptureRequest: jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
result: {
|
||||
id: "test",
|
||||
},
|
||||
capture: true,
|
||||
}
|
||||
}),
|
||||
CapturesRefundRequest: jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
result: {
|
||||
id: "test",
|
||||
},
|
||||
body: null,
|
||||
requestBody: function (d) {
|
||||
this.body = d
|
||||
},
|
||||
}
|
||||
}),
|
||||
},
|
||||
|
||||
orders: {
|
||||
OrdersCreateRequest: jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
result: {
|
||||
id: "test",
|
||||
},
|
||||
order: true,
|
||||
body: null,
|
||||
requestBody: function (d) {
|
||||
this.body = d
|
||||
},
|
||||
}
|
||||
}),
|
||||
OrdersPatchRequest: jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
result: {
|
||||
id: "test",
|
||||
},
|
||||
order: true,
|
||||
body: null,
|
||||
requestBody: function (d) {
|
||||
this.body = d
|
||||
},
|
||||
}
|
||||
}),
|
||||
OrdersGetRequest: jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
result: {
|
||||
id: "test",
|
||||
},
|
||||
}
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
export default PayPalMock
|
||||
@@ -0,0 +1,346 @@
|
||||
import PayPalProviderService from "../paypal-provider"
|
||||
import {
|
||||
PayPalMock,
|
||||
PayPalClientMock,
|
||||
} from "../../__mocks__/@paypal/checkout-server-sdk"
|
||||
|
||||
const TotalsServiceMock = {
|
||||
getTotal: jest.fn().mockImplementation((c) => c.total),
|
||||
}
|
||||
|
||||
const RegionServiceMock = {
|
||||
retrieve: jest.fn().mockImplementation((id) =>
|
||||
Promise.resolve({
|
||||
currency_code: "eur",
|
||||
})
|
||||
),
|
||||
}
|
||||
|
||||
describe("PaypalProviderService", () => {
|
||||
describe("createPayment", () => {
|
||||
let result
|
||||
const paypalProviderService = new PayPalProviderService(
|
||||
{
|
||||
regionService: RegionServiceMock,
|
||||
totalsService: TotalsServiceMock,
|
||||
},
|
||||
{
|
||||
api_key: "test",
|
||||
}
|
||||
)
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("creates paypal order", async () => {
|
||||
result = await paypalProviderService.createPayment({
|
||||
id: "test_cart",
|
||||
region_id: "fr",
|
||||
total: 1000,
|
||||
})
|
||||
|
||||
expect(PayPalMock.orders.OrdersCreateRequest).toHaveBeenCalledTimes(1)
|
||||
expect(PayPalClientMock.execute).toHaveBeenCalledTimes(1)
|
||||
expect(PayPalClientMock.execute).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
order: true,
|
||||
body: {
|
||||
intent: "AUTHORIZE",
|
||||
application_context: {
|
||||
shipping_preference: "NO_SHIPPING",
|
||||
},
|
||||
purchase_units: [
|
||||
{
|
||||
custom_id: "test_cart",
|
||||
amount: {
|
||||
currency_code: "EUR",
|
||||
value: "10.00",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
expect(result.id).toEqual("test")
|
||||
})
|
||||
})
|
||||
|
||||
describe("retrievePayment", () => {
|
||||
let result
|
||||
const paypalProviderService = new PayPalProviderService(
|
||||
{
|
||||
regionService: RegionServiceMock,
|
||||
totalsService: TotalsServiceMock,
|
||||
},
|
||||
{
|
||||
api_key: "test",
|
||||
}
|
||||
)
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("retrieves paypal order", async () => {
|
||||
result = await paypalProviderService.retrievePayment({ id: "test" })
|
||||
expect(PayPalMock.orders.OrdersGetRequest).toHaveBeenCalledTimes(1)
|
||||
expect(PayPalMock.orders.OrdersGetRequest).toHaveBeenCalledWith("test")
|
||||
expect(PayPalClientMock.execute).toHaveBeenCalledTimes(1)
|
||||
|
||||
expect(result.id).toEqual("test")
|
||||
})
|
||||
})
|
||||
|
||||
describe("updatePayment", () => {
|
||||
let result
|
||||
const paypalProviderService = new PayPalProviderService(
|
||||
{
|
||||
regionService: RegionServiceMock,
|
||||
totalsService: TotalsServiceMock,
|
||||
},
|
||||
{
|
||||
api_key: "test",
|
||||
}
|
||||
)
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("updates paypal order", async () => {
|
||||
result = await paypalProviderService.updatePayment(
|
||||
{ id: "test" },
|
||||
{
|
||||
id: "test_cart",
|
||||
region_id: "fr",
|
||||
total: 1000,
|
||||
}
|
||||
)
|
||||
|
||||
expect(PayPalMock.orders.OrdersPatchRequest).toHaveBeenCalledTimes(1)
|
||||
expect(PayPalMock.orders.OrdersPatchRequest).toHaveBeenCalledWith("test")
|
||||
expect(PayPalClientMock.execute).toHaveBeenCalledTimes(1)
|
||||
expect(PayPalClientMock.execute).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
order: true,
|
||||
body: [
|
||||
{
|
||||
op: "replace",
|
||||
path: "/purchase_units/@reference_id=='default'",
|
||||
value: {
|
||||
amount: {
|
||||
currency_code: "EUR",
|
||||
value: "10.00",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
)
|
||||
|
||||
expect(result.id).toEqual("test")
|
||||
})
|
||||
})
|
||||
|
||||
describe("capturePayment", () => {
|
||||
let result
|
||||
const paypalProviderService = new PayPalProviderService(
|
||||
{
|
||||
regionService: RegionServiceMock,
|
||||
totalsService: TotalsServiceMock,
|
||||
},
|
||||
{
|
||||
api_key: "test",
|
||||
}
|
||||
)
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("updates paypal order", async () => {
|
||||
result = await paypalProviderService.capturePayment({
|
||||
data: {
|
||||
id: "test",
|
||||
purchase_units: [
|
||||
{
|
||||
payments: {
|
||||
authorizations: [
|
||||
{
|
||||
id: "test_auth",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
expect(
|
||||
PayPalMock.payments.AuthorizationsCaptureRequest
|
||||
).toHaveBeenCalledTimes(1)
|
||||
expect(
|
||||
PayPalMock.payments.AuthorizationsCaptureRequest
|
||||
).toHaveBeenCalledWith("test_auth")
|
||||
expect(PayPalMock.orders.OrdersGetRequest).toHaveBeenCalledWith("test")
|
||||
expect(PayPalClientMock.execute).toHaveBeenCalledTimes(2)
|
||||
|
||||
expect(result.id).toEqual("test")
|
||||
})
|
||||
})
|
||||
|
||||
describe("refundPayment", () => {
|
||||
let result
|
||||
const paypalProviderService = new PayPalProviderService(
|
||||
{
|
||||
regionService: RegionServiceMock,
|
||||
totalsService: TotalsServiceMock,
|
||||
},
|
||||
{
|
||||
api_key: "test",
|
||||
}
|
||||
)
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("refunds payment", async () => {
|
||||
result = await paypalProviderService.refundPayment(
|
||||
{
|
||||
currency_code: "eur",
|
||||
data: {
|
||||
id: "test",
|
||||
purchase_units: [
|
||||
{
|
||||
payments: {
|
||||
captures: [
|
||||
{
|
||||
id: "test_cap",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
2000
|
||||
)
|
||||
|
||||
expect(PayPalMock.payments.CapturesRefundRequest).toHaveBeenCalledWith(
|
||||
"test_cap"
|
||||
)
|
||||
expect(PayPalMock.orders.OrdersGetRequest).toHaveBeenCalledWith("test")
|
||||
expect(PayPalClientMock.execute).toHaveBeenCalledTimes(2)
|
||||
expect(PayPalClientMock.execute).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
body: {
|
||||
amount: {
|
||||
currency_code: "EUR",
|
||||
value: "20.00",
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
expect(result.id).toEqual("test")
|
||||
})
|
||||
|
||||
it("doesn't refund without captures", async () => {
|
||||
await expect(
|
||||
paypalProviderService.refundPayment(
|
||||
{
|
||||
currency_code: "eur",
|
||||
data: {
|
||||
id: "test",
|
||||
purchase_units: [
|
||||
{
|
||||
payments: {
|
||||
captures: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
2000
|
||||
)
|
||||
).rejects.toThrow("Order not yet captured")
|
||||
})
|
||||
})
|
||||
|
||||
describe("cancelPayment", () => {
|
||||
let result
|
||||
const paypalProviderService = new PayPalProviderService(
|
||||
{
|
||||
regionService: RegionServiceMock,
|
||||
totalsService: TotalsServiceMock,
|
||||
},
|
||||
{
|
||||
api_key: "test",
|
||||
}
|
||||
)
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("refunds if captured", async () => {
|
||||
result = await paypalProviderService.cancelPayment({
|
||||
captured_at: "true",
|
||||
currency_code: "eur",
|
||||
data: {
|
||||
id: "test",
|
||||
purchase_units: [
|
||||
{
|
||||
payments: {
|
||||
captures: [
|
||||
{
|
||||
id: "test_cap",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
expect(PayPalMock.payments.CapturesRefundRequest).toHaveBeenCalledWith(
|
||||
"test_cap"
|
||||
)
|
||||
expect(PayPalMock.orders.OrdersGetRequest).toHaveBeenCalledWith("test")
|
||||
expect(PayPalClientMock.execute).toHaveBeenCalledTimes(2)
|
||||
|
||||
expect(result.id).toEqual("test")
|
||||
})
|
||||
|
||||
it("voids if not captured", async () => {
|
||||
result = await paypalProviderService.cancelPayment({
|
||||
currency_code: "eur",
|
||||
data: {
|
||||
id: "test",
|
||||
purchase_units: [
|
||||
{
|
||||
payments: {
|
||||
authorizations: [
|
||||
{
|
||||
id: "test_auth",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
expect(
|
||||
PayPalMock.payments.AuthorizationsVoidRequest
|
||||
).toHaveBeenCalledWith("test_auth")
|
||||
expect(PayPalMock.orders.OrdersGetRequest).toHaveBeenCalledWith("test")
|
||||
expect(PayPalClientMock.execute).toHaveBeenCalledTimes(2)
|
||||
|
||||
expect(result.id).toEqual("test")
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -5,7 +5,7 @@ import { PaymentService } from "medusa-interfaces"
|
||||
class PayPalProviderService extends PaymentService {
|
||||
static identifier = "paypal"
|
||||
|
||||
constructor({ customerService, totalsService, regionService }, options) {
|
||||
constructor({ totalsService, regionService }, options) {
|
||||
super()
|
||||
|
||||
/**
|
||||
@@ -35,9 +35,6 @@ class PayPalProviderService extends PaymentService {
|
||||
/** @private @const {PayPalHttpClient} */
|
||||
this.paypal_ = new PayPal.core.PayPalHttpClient(environment)
|
||||
|
||||
/** @private @const {CustomerService} */
|
||||
this.customerService_ = customerService
|
||||
|
||||
/** @private @const {RegionService} */
|
||||
this.regionService_ = regionService
|
||||
|
||||
@@ -196,7 +193,7 @@ class PayPalProviderService extends PaymentService {
|
||||
value: {
|
||||
amount: {
|
||||
currency_code: currency_code.toUpperCase(),
|
||||
value: (cart.total / 100).toFixed(),
|
||||
value: (cart.total / 100).toFixed(2),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -240,7 +237,7 @@ class PayPalProviderService extends PaymentService {
|
||||
* Refunds a given amount.
|
||||
* @param {object} payment - payment to refund
|
||||
* @param {number} amountToRefund - amount to refund
|
||||
* @returns {string} the resulting PayPal order
|
||||
* @returns {Promise<Object>} the resulting PayPal order
|
||||
*/
|
||||
async refundPayment(payment, amountToRefund) {
|
||||
const { purchase_units } = payment.data
|
||||
@@ -257,13 +254,13 @@ class PayPalProviderService extends PaymentService {
|
||||
request.requestBody({
|
||||
amount: {
|
||||
currency_code: payment.currency_code.toUpperCase(),
|
||||
value: (amountToRefund / 100).toFixed(),
|
||||
value: (amountToRefund / 100).toFixed(2),
|
||||
},
|
||||
})
|
||||
|
||||
await this.paypal_.execute(request)
|
||||
|
||||
return this.retrievePayment(payment.id)
|
||||
return this.retrievePayment(payment.data)
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
@@ -272,7 +269,7 @@ class PayPalProviderService extends PaymentService {
|
||||
/**
|
||||
* Cancels payment for Stripe payment intent.
|
||||
* @param {object} paymentData - payment method data from cart
|
||||
* @returns {object} canceled payment intent
|
||||
* @returns {Promise<object>} canceled payment intent
|
||||
*/
|
||||
async cancelPayment(payment) {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user