Skip to content

Conversation

Kenneth-W-Chen
Copy link
Member

@Kenneth-W-Chen Kenneth-W-Chen commented Sep 1, 2024

Summary

  • Uses PayPal REST API since PayPal deprecated their ButtonManager and NVP/SOAP APIs
    • PayPalCustomApi (paypal.php) serves as an intermediary for the API. Currently implements getting an OAuth token, creating orders, getting order details, and capturing payment.
    • Created some objects to represent API request body information (paypal/api-json-objects.php)
  • Orders are created when the user presses the PayPal button.
    • Users are sent to /paypal/get-order which sets the Location header to the PayPal checkout link.
  • IPN replaced with webhook event handling at /paypal/event-notify.
    • IPN handlers have been moved to webhook/handlers. The functionality remains mostly the same.

Missing functionalities (won't be added in this ticket)

  • Integration with API endpoints that are not orders and webhooks
    • Missing from orders: Update Orders, Confirm the Order, Authorize payment for order, Add tracking information for an Order, Update or cancel tracking information for a PayPal Order.
    • Missing from webhooks: everything except Verify webhook signature.
  • Payment methods besides PayPal are not implemented. Apple/Google Pay won't be implemented in this PR.
  • Removed order info output on the /merch/buy/complete page because
    • Payment details aren't available at this point and order ID isn't usable by the customer
    • Retrieving product info would require a call to the PayPal and Printful APIs

Other changes

  • config.php
    • Defined constants PAYPAL_SANDBOX_API_CLIENT_ID, PAYPAL_SANDBOX_API_SECRET_KEY_1, PAYPAL_SANDBOX_API_ACCOUNT, PAYPAL_SANDBOX_API_PASSWORD, PAYPAL_SANDBOX_API_SIGNATURE, and PAYPAL_SANDBOX_WEBHOOK_ID
  • Removed pointer-events: none from .paypal-button-overlay for the PayPal button since the button is now an actual button
  • Changed receipt email flow to only send one receipt instead of multiple per transaction.
    • Moved it to a separate function in /template/functions/paypal.php.
    • The email is sent as an attachment to the web-dev-logs channel if it's in the dev environment.
  • Dues T-shirt discount is now actually shown on the PayPal receipt as a separate field. The amount paid is still the same.
  • Removed an error thrown in the Printful handler from when the revenue is less than the total cost of the product.
    • This ends up being the case most of the time because of shipping+tax exceeding Printful's suggested retail price
    • Swapped it to a discord message that also lists the shipping and tax

Testing

  • Test PayPalCustomApi functionality
  • Test PayPalWebhookEvent functionality
  • Test button creation on frontend
  • Test webhook event filtering works
  • Test that order creation works through the button
  • Test the dues webhook (formerly IPN) handler
  • Test the Printful webhook (formerly IPN) handler
    • Test that Printful items are added to db properly (/merch/product and function insert_paypal_item())
    • Test that Printful orders are properly made
  • Check that the email HTML works
    • Check Dues-only receipt
    • Check Printful-only receipt
    • Check Dues and Printful receipt

Order process (flowchart updated 10/11/2024)

paypal-api-flow

API minor notes

  • If you create an order that needs shipping info but don't provide it, the response from create_order will return a checkout link where the payer will provide their shipping info and payment info. Payment source info in capture_payment does not need to be filled out in this case (although you still need to pick a payment source [e.g., PayPal or Apple Pay]).
  • Tax isn't a necessary field for the API.

Receipt notes

  • Technically the buyer receives a receipt from PayPal too with the items they bought. Except for Printful information (IDs and shipping), everything else is likely duplicated
  • "Order Name" will be a comma-delimited list of the order items. E.g., "Dues, Dues Shirt" will be the order name

Usage

Create orders

Create a PayPalCustomApi object and call create_order() with the appropriate arguments. The response is JSON-encoded string with the order ID, status, and links to checkout and do other things.
Example response:

{
  "id": "123ABC456DEF",
  "status": "PAYER_ACTION_REQUIRED",
  "payment_source": {
    "paypal": {}
  },
  "links": [
    {
      "href": "https://api.sandbox.paypal.com/v2/checkout/orders/123ABC456DEF",
      "rel": "self",
      "method": "GET"
    },
    {
      "href": "https://www.sandbox.paypal.com/checkoutnow?token=123ABC456DEF",
      "rel": "payer-action",
      "method": "GET"
    }
  ]
}

Capture payments

Call this function on an order ID after the payer has approved payment.

require_once('../api/paypal/paypal.php');

$p = new PayPalCustomApi();
$p->capture_payment($_GET['token'],true);

You should check the status field returned by the capture request. Specifically, you want COMPLETED. See the docs for more details.

Get order info

Call this on an order ID to get information on the order from PayPal.

require_once('path/to/dir/api/paypal/paypal.php');
$p = new PayPalCustomApi();
$r = $p->get_order_info('orderid');

The result will be a JSON-encoded string. It's a bit long, so I'm not including a sample here.

Creating a payment button

require_once('path/to/dir/template/functions/paypal.php');
$items = [['type':'dues', 'full_year':true, 't-shirt':false], ['type':'printful','ext_id':'externalid' , 'variant_id':'variantid'], ['type':'donation', 'amount':'99.99'], ...];
get_payment_button('text to display', $items, 'return/page/uri', 'cancel/page/uri');
  • text to display is the text displayed on the button. All letters will be capitalized.
  • $items is an array of the items to send. The format for all implemented types is shown.
    • The array should be a 2-D array with each 1-D array representing a single item.
    • To change a value with JavaScript, access the input's value with input#paypal-items<index><key> e.g., input#paypal-items0full_year
  • return/page/uri is the page to return the user to upon completing (approving) the order. This is relative to the website's server name (e.g., www.untrobotics.com)
  • cancel/page/uri is the page to return the user to upon cancelling the order. It follows the same rules as return/page/uri

To-do

@Kenneth-W-Chen
Copy link
Member Author

7bf6ff4 adds a helper function that's in #158. Once that's merged, I'll remove the one at the bottom of api/paypal/paypal.php and call the other one. I'll also run the requests through the caching system, although the only thing we'll cache is probably info from get_order

Catch error from send_request in create_order
Fixed issue with create_order that threw an unexpected error
@Kenneth-W-Chen
Copy link
Member Author

There's a way to verify webhook events without PayPal's API, but it does require PayPal's certificate. Is there a folder we store certs in already or should I store it in a new folder?

@Kenneth-W-Chen
Copy link
Member Author

I stopped having fun about 30 minutes ago

@Kenneth-W-Chen
Copy link
Member Author

Me having fun now

Receipt email is only sent once now.
Removed $fields parameter from get_order_info since you can only filter for "payment_source" (which only returns the order ID).
Added variant_name to paypal_items table. Added variant stuff to the order creation process
Added function to tell if a webhook event is sandbox.
Stopped webhook handler from trying to capture payment we already marked as captured
Made a new payment button function for when the page has static info on the order (i.e., Printful products and dues page during the Spring)
Moved the IPN handlers to webhook/handlers/
@Kenneth-W-Chen
Copy link
Member Author

I should've made the commit message for f58829b say "Fixed some stuff"

@Kenneth-W-Chen Kenneth-W-Chen marked this pull request as ready for review September 21, 2024 02:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants