Import PayPal transactions as Shopify orders, with Mechanic.

Mechanic is a development platform for Shopify. :)

Import PayPal transactions as Shopify orders

by Isaac Bowen (team@usemechanic.com)

Use this task when you want to record your external PayPal transactions as Shopify orders. Are you using PayPal subscriptions? Or receiving direct payments you'd like to record in Shopify? Use this task, or file a task request to have it modified for more purpose-specific uses. :) Again, this isn't for PayPal as a Shopify payment provider – it's for any time you might be using PayPal *outside* of your Shopify store.

Runs when user/paypal/ipn is triggered. Configuration includes only import these transaction types, only import completed transactions, send receipt, default memo, order requires shipping, mark as fulfilled, and send fulfillment receipt.

15-day free trial – unlimited tasks

Documentation

To get set up, follow our tutorial: Connecting PayPal to Mechanic.

Then, configure to taste. :) Mechanic will use the PayPal order memo as the sole line item in the orders that are created, falling back to your "Default memo" setting.

Out of the box, this task will only create orders for transactions in which payment successfully went through. To modify this task to perform extra functionality (like auto-tagging based on subscription events, for example), file a task request from your Mechanic home screen.

Developer details

Mechanic is designed to benefit everybody: merchants, customers, developers, agencies, Gurus, everybody.

That’s why we make it easy to configure automation without code, why we make it easy to tweak the underlying code once tasks are installed, and why we publish it all here for everyone to learn from.

Events
when user/paypal/ipn is triggered (user/paypal/ipn)
Options
only import these transaction types (array), only import completed transactions (boolean), send receipt (boolean), default memo, order requires shipping (boolean), mark as fulfilled (boolean), send fulfillment receipt (boolean)
Script
{% comment %}
  Hello, developer friend! :) Here's the PayPal IPN variable reference:

  https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNandPDTVariables

  ... and here's their IPN payload simulator:

  https://developer.paypal.com/developer/ipnSimulator/

  Their default payment_date value doesn't even meet *their* standard, so change it to
  something like "22:03:27 Sep 28, 2018 PDT" if you need to test that value. <3
{% endcomment %}

{% if event.preview %}
  {% capture event_data_json %}
    {
      "mc_gross": "-132.29",
      "invoice": "CF customer@example.com_[12345]_12345",
      "settle_amount": "92.59",
      "protection_eligibility": "Eligible",
      "item_number1": "",
      "payer_id": "ABCDEFABCDEF",
      "address_street": "1234 Street Lane",
      "payment_date": "00:00:00 Jan 01, 2000 UTC",
      "payment_status": "Completed",
      "charset": "windows-1252",
      "address_zip": "12345",
      "mc_shipping": "0.00",
      "mc_handling": "0.00",
      "first_name": "Customer",
      "mc_fee": "-6.78",
      "address_country_code": "US",
      "exchange_rate": "0.737685",
      "address_name": "Customer Name",
      "notify_version": "3.9",
      "reason_code": null,
      "settle_currency": "GBP",
      "custom": "",
      "business": "customer@example.com",
      "address_country": "",
      "mc_handling1": "0.00",
      "address_city": "Billingsly",
      "verify_sign": "B8xyEug85joDO4J7jO5XTilRUzIiAZ.xQozW-0M7PB9q4Z117AtHGEtx",
      "payer_email": "customer@example.com",
      "mc_shipping1": "0.00",
      "tax1": "0.00",
      "parent_txn_id": "ABC123ABC123ABC123ABC123",
      "txn_id": "123ABC123ABC123ABC123ABC",
      "payment_type": {{ options.only_import_these_transaction_types__array.first | default: "invoice_payment" | json }},
      "last_name": "Customer",
      "address_state": "ST",
      "item_name1": "Item name",
      "receiver_email": "customer@example.com",
      "payment_fee": "-6.78",
      "shipping_discount": "0.00",
      "quantity1": "1",
      "insurance_amount": "0.00",
      "receiver_id": "ABCDEFABCDEF",
      "pending_reason": null,
      "discount": "0.00",
      "mc_gross_1": "132.29",
      "mc_currency": "USD",
      "residence_country": "US",
      "shipping_method": "",
      "transaction_subject": "",
      "payment_gross": "-132.29",
      "ipn_track_id": "abcdef123456"
    }
  {% endcapture %}

  {% assign event = hash %}
  {% assign event["data"] = event_data_json | parse_json %}
{% endif %}

{% assign import_order = true %}

{% if options.only_import_completed_transactions__boolean and event.data.payment_status != "Completed" %}
  {% log message: "This transaction's payment status is not Completed; skipping", payment_status: event.data.payment_status %}
  {% assign import_order = false %}
{% endif %}

{% comment %}Some notifications have a txn_type, some have a payment_type.{% endcomment %}
{% assign transaction_type = event.data.txn_type | default: event.data.payment_type %}

{% if options.only_import_these_transaction_types__array != blank %}
  {% unless options.only_import_these_transaction_types__array contains transaction_type %}
    {% log message: "This transaction's type was not on the list; skipping", transaction_type: transaction_type %}
    {% assign import_order = false %}
  {% endunless %}
{% endif %}

{% if import_order %}
  {% comment %}
    PayPal's date format is non-standard: `HH:MM:SS Mmm DD, YYYY PDT`, per their docs.
    We reformat it here, to ensure that Liquid can parse it for Shopify-ready formatting.
  {% endcomment %}
  {% assign date_parts = event.data.payment_date | replace: ",", "" | split: " " %}
  {% capture sane_payment_date %}{{ date_parts[3] }} {{ date_parts[1] }} {{ date_parts[2] }} {{ date_parts[0] }} {{ date_parts[4] }}{% endcapture %}

  {% action "shopify" %}
    [
      "create",
      "order",
      {
        "email": {{ event.data.payer_email | json }},
        "send_receipt": {{ options.send_receipt__boolean | json }},
        "processed_at": {{ sane_payment_date | date: "%Y-%m-%dT%H:%M:%S.%L%z" | json }},
        "financial_status": "paid",
        "currency": {{ event.data.mc_currency | json }},
        "line_items": [
          {
            "title": {{ event.data.memo | default: options.default_memo | default: "(no memo given)" | json }},
            "price": {{ event.data.mc_gross | divided_by: 1.0 | json }},
            "requires_shipping": {{ options.order_requires_shipping__boolean | json }},
            "quantity": 1,
            "taxable": false
          }
        ],
        "fulfillment_status": {% if options.mark_as_fulfilled__boolean %}"fulfilled"{% else %}null{% endif %},
        "send_fulfillment_receipt": {{ options.send_fulfillment_receipt__boolean | json }},
        "transactions": [
          {
            "kind": "sale",
            "status": "success",
            "amount": {{ event.data.mc_gross | divided_by: 1.0 | json }}
          }
        ],
        "customer": {
          "id": {{ shop.customers[event.data.payer_email].id | json }}
        },
        "billing_address": {
          "first_name": {{ event.data.first_name | json }},
          "last_name": {{ event.data.last_name | json }},
          "address1": {{ event.data.address_street | json }},
          "city": {{ event.data.address_city | json }},
          "province": {{ event.data.address_state | json }},
          "country": {{ event.data.address_country | json }},
          "zip": {{ event.data.address_zip | json }}
        },
        {% if options.order_requires_shipping__boolean %}
          "shipping_address": {
            "first_name": {{ event.data.first_name | json }},
            "last_name": {{ event.data.last_name | json }},
            "address1": {{ event.data.address_street | json }},
            "city": {{ event.data.address_city | json }},
            "province": {{ event.data.address_state | json }},
            "country": {{ event.data.address_country | json }},
            "zip": {{ event.data.address_zip | json }}
          },
        {% endif %}
        "note_attributes": [
          {
            "name": "PayPal protection eligibility",
            "value": {{ event.data.protection_eligibility | json }}
          },
          {
            "name": "PayPal payer ID",
            "value": {{ event.data.payer_id | json }}
          },
          {
            "name": "PayPal transaction ID",
            "value": {{ event.data.txn_id | json }}
          },
          {
            "name": "PayPal receiver ID",
            "value": {{ event.data.receiver_id | json }}
          },
          {
            "name": "PayPal transaction type",
            "value": {{ event.data.txn_type | json }}
          }
        ]
      }
    ]
  {% endaction %}
{% endif %}
Mechanic tasks are written in Liquid, which makes them easy to write and easy to modify. Learn more about our platform.
Defaults
Only import completed transactions
true
Default memo
Your PayPal payment