Skip to content

PayPal Refunds

PayPal only allows for refunds within 180 days of purchase. If we need to refund after the 180 days have passed, the only way to do a manual payout instead.

Step 1: Determine the users' PayPal account

We first need to reach out to the user to determine what the user's PayPal account email address is before we can initiate the payout. Once we know what it is, it needs to be saved for the given user before we run step 2 in this process. It can be saved on the edit member page on Nova:

payments_paypal-refunds_paypal-account-nova

Step 2: Create a credit note

We need to create a credit note before initiating the refund. To do so, view the details for the invoice that you are refunding on Nova, click on the 3 dots on the right-hand side of the screen and select 'Create Credit Note for the corresponding payment (without refund)':

payments_paypal-refunds_create-credit-note

On the modal that pops-up, keep the default options as they are and create the credit note.

payments_paypal-refunds_credit-note-modal

Once the credit note is created, take note of the ID - you will need it in step 4.

Step 3: Run payout script

Note: This step requires writing and running a snippet of code, so it can only be done by a developer.

The following script needs to be run in Tinker on the production server:

php
$pg = new PayPalDenmarkGateway();
$t = $pg->payout(Member::withoutGlobalScopes()->withTrashed()->find(MEMBER_ID), \App\Modules\Money\Entities\MonetaryAmount::createFromMinorUnits(REFUND_AMOUNT, \App\Modules\Money\Models\Currency::findByAlphabeticalCode('REFUND_CURRENCY_CODE')), 'Refund for Invoice INVOICE_NUMBER');

You'll need to replace the placeholders with valid data for the given refund:

  • MEMBER_ID: the ID for the member in the database, e.g 108626
  • REFUND_AMOUNT: the amount to be refunded, with an underscore separator e.g 135_00, 550_00.
  • REFUND_CURRENCY_CODE: The code for the currency to issue the currency in, e.g EUR, USD
  • INVOICE_NUMBER: The number for the invoice, e.g # US-227430

WARNING

Once you run this, some events triggered by PayPal webhooks are going to fail. Don't panic - this will be resolved in step 4 😃

Step 4: Fix credit note and transaction data in the DB

Note: This step requires manual changes in the database, so it can only be done by a developer.

When you completed step 3, the console should have shown you the new transaction data. Take note of the ID, as you will need to find the newly created transaction with it in the DB.

Fix transaction data

  1. Open your database tool of choice
  2. Open the payment__transactions table
  3. Search for the newly created transaction using the ID
  4. Add the ID of the credit note you created in step 2 in the order_id column
  5. Find the ID of the paid transaction linked to the original invoice and add it in the charge_transaction_id column

Fix credit note data

  1. Open your database tool of choice (if not open already)
  2. Open the payment__orders table
  3. Search for the credit note you created in step 2 using it's ID
  4. Enter the ID of the transaction in paid_transaction_id
  5. Set paid_at field to null - step 2 automatically added a date, but the PayPal webhooks will fail to update the credit note/transaction without removing this value.

WARNING

You might see an error in #errors-production about a FillTransactionFeeUsd job failing. This happens because of a previously failed attempt to add the PayPal gateway fee via a webhook listener.

If this occurs, you can login to the PayPal dashboard, find the transaction, check the gateway fee (stored as payout_item_fee in the transaction native metadata) and then store it as the value of gateway_fee_as_minor for the transaction in the DB. Make sure you convert it to minor format first. You'll also need to check the currency of the fee and add that as the value of gateway_fee_currency_code.

You may also be able to find the gateway fee in the logs in #errors-production instead of needing to login to the PayPal dashboard