Skip to content

v1.0.0

Latest
Compare
Choose a tag to compare
@Aman-Aalam Aman-Aalam released this 12 Sep 23:17
d8119af

Trolley Python SDK Major Update (Sept 12, 2023)

With this update we're making some important changes to our Python SDK.
This update covers all the API endpoints our SDK didn't cover before. We are also renaming all instances of PaymentRails to Trolley to complete our rebranding.

To mark this milestone we're moving to our first major version 1.0.0, and are releasing this version under a new name.

Please read this Update Guide to understand what changed and how to adopt these changes.

Breaking Changes

1. New package on PyPI

Our Python SDK is now available under a new namespace on PyPI, which is as per our new name 'Trolley' : https://pypi.org/project/trolleyhq/

With this update, you'll have to make changes to how Trolley SDK is referenced and used in your code:

  • The command you used to install the SDK now changes:
Old Installation New Installation
pip install paymentrails pip install trolleyhq
  • If you're adding the package directly in your requirements.txt you'll need to replace it with the new package name:
Old Package New Package
paymentrails>=0.2 trolleyhq>=1.0

2. All “PaymentRails” instances renamed to “Trolley”

To complete our rebranding, all the classes and packages called PaymentRails are now called Trolley. You'll need to update the references in your code.

Old usage

from paymentrails.configuration import Configuration
from paymentrails.recipients import Recipients

client = Configuration.gateway("ACCESS_KEY","SECRET_KEY")
response = client.recipient.find("R-WJniNqUXyboAimmJ4")

New usage

from trolley.configuration import Configuration
from trolley.recipients import Recipients

client = Configuration.gateway("ACCESS_KEY","SECRET_KEY")
response = client.recipient.find("R-WJniNqUXyboAimmJ4")

A simple find-and-replace should be helpful in replacing the class names.

3. Default environment for Configuration

The SDK now defaults to the production URL i.e. api.trolley.com.
So now, you no longer need to provide an environment.

If you're running from the source and need to specify a server url, you now have to create a .env file in the project root and specify the SERVER_URL there. Then, specify the environment as development for the SDK to start picking up the API url from .env file.

Old way

client = Configuration.gateway('ACCESS_KEY', 'SECRET_KEY', 'PRODUCTION')

OR

config = Configuration('ACCESS_KEY', 'SECRET_KEY', 'PRODUCTION')

New way

client = Configuration.gateway('ACCESS_KEY', 'SECRET_KEY')

OR

config = Configuration('ACCESS_KEY', 'SECRET_KEY')

4. Updates to search() strategy

The search() method in different gateway classes now returns a generator defaulting to pageSize of 10.

So now when you use the search() method, you'll get a generator which you can iterate through with a loop.

This generator will automatically advance to the next page within the loop, so you don't have to paginate manually.

This is perfect for going over all the items in a search result without worrying about pagination.

For example:

payments = self.client.payment.search("<batch-id>")

# print all payments page by page
for payment in payments:
  print(payment.id)

If you're looking to paginate manually, you should use search_by_page() method available in all the gateway classes that support search or pagination.

payments = self.client.payment.search_by_page("<batch-id>","<search-term>",1,10)

5. balances_gateway.find(term) replaced

Balances have seen an update, and the balances_gateway.find() method no longer exists.

It's being replaced by balances_gateway.get_all_balances() which fetches all Trolley account balances and return an array containing them.

You can provide a search term to this method to fetch any specific account balance (paypal or trolley etc) but by default it fetches all the accounts' balances.

This gateway now also contains new methods to fetch individual account balances, please read the New Methods section for it.

6. Types classes have their own package now

The types classes (e.g. batch.py, payment.py etc.) have been moved to a package/folder called types and static methods in these files have been removed.

This is done in order to remove the redundancy and only have *_gateway.py classes contain the methods making API calls.

This is also in line with the SDK documentation mentioned in our README file.

Old Usage
# accessing the static find() method of the Payment class, which in turn accesses the find() method of PaymentGateway class
payment = trolley.Payment.find(<payment_id>, <batch_id>)
New Usage
# accessing the find() method of PaymentGateway class directly
payment = config.payment.find(<payment_id>, <batch_id>)

Deprecated Methods

The following methods have been deprecated and will be removed in future releases:

[DEPRECATED] recipient_account_gateway.findAll(<recipient_id>)

Update to existing Methods

1. Support for Errors as Arrays

The API returns errors in a JSON array. To support this, we have added a way for you to access the errors as an Array as well.
This should make it easier for you to iterate through the array and process the errors.

The array can be accessed through the get_error_array() method available through all the Exceptions that Trolley SDK exposes.

Example: Consuming error Array

...
except MalformedException as e:
    print(e.get_error_array())
    print(e.get_error_array()[0]['message'])
...

New Methods/API Coverage

We have added support for more API endpoints that our Python SDK wasn't supporting earlier.

Here's a rundown of the new API endpoints covered:

Recipient

1. Delete Multiple Recipients

payload = {
      "ids": [
          recipient1.id,
          recipient2.id
          ]
      }
delete_result = config.recipient.delete_multiple(payload)

2. Retrieve All Logs

recipient_logs = config.recipient.retrieve_logs('R-recipient_id',1,20)

3. Retrieve All Payments of Recipient

recipient_payments = config.recipient.get_all_payments("R-recipient_id")

# iterate through the returned payments
for payment in recipient_payments:
  print(payment.id)

4. Retrieve recipient’s offline payments

offline_payments = config.recipient.get_all_offline_payments("R-recipient_id")

# iterate through the returned payments
for payment in offline_payments:
  print(payment.id)

Batch

5. Delete multiple batches

response = config.batch.delete_multiple({
        "ids":[
            batch1.id,
            batch2.id
    ]})

6. List all Batches

batches = config.batch.list_all_batches()

# iterate through the returned generator
for batch in batches:
  print(batch.id)

Payment

7. List all Payments

payments = config.payment.list_all_payments(batch.id)

# iterate through the returned generator
for payment in payments:
  print(payment.id)

Offline Payment

8. Create OfflinePayment

offline_payment = config.offline_payment.create(
     recipient.id, 
     {
        "currency":"CAD",
	"amount":"10.00",
	"payoutMethod":"paypal",
	"category":"services",
	"memo":"offline payment memo",
	"processedAt":"2022-06-22T01:10:17.571Z"
     })

9. List OfflinePayment

offline_payments = config.offline_payment.get_all()

# iterate through the returned generator
for payment in offline_payments:
  print(payment.id)

OR, get by page:

offline_payments = self.client.offline_payment.get_all_by_page(2, 10)

# iterate through the returned list
for payment in offline_payments:
  print(payment.id)

10. Update OfflinePayment

offline_payment = config.offline_payment.update(
    offline_payment.id,
    recipient_id, 
    {
      "currency":"CAD",
      "amount":"20.00"
    })

11. Delete OfflinePayment

del_result = config.offline_payment.delete(recipient.id, offline_payment.id)

Invoices

12. Create an Invoice

invoice = config.invoice.create({
    "recipientId": recipient.id,
    "description": "Invoice created from Python SDK"
    })

13. Fetch an Invoice

invoice = config.invoice.get(invoice.id)

14. Search Invoice

invoices = self.client.invoice.search({
      "externalIds": [
          f"{externalId}"
      ]
  })

# iterate through the returned generator
for invoice in invoices:
  print(invoice.id)

15. Update an Invoice

invoice = config.invoice.update(
  invoice.id,
  {
      "externalId": externalId
  })

16. Delete an Invoice

del_result = config.invoice.delete([
      f'{invoice.id}',
      f'{invoice2.id}'
  ])

Invoice line

17. Create an Invoice line

from trolley.types.invoice_line import InvoiceLine

invoice = config.invoice_line.create(
  invoice.id,
  [
      {
          "unitAmount" :{
              "value" : "250.00",
              "currency" : "EUR"
          },
          "description" : "first line",
          "category": InvoiceLine.categories.education
      }
  ])

18. Update one/multiple invoice lines

from trolley.types.invoice_line import InvoiceLine

invoice = config.invoice_line.update(
  invoice.id,
  [
      {
          "invoiceLineId" : invoice.id,
          "unitAmount" :{
              "value" : "151.00",
              "currency" : "EUR"
          },
          "category" : InvoiceLine.categories.refunds
      }
  ])

19. Delete an invoice line

del_result = config.invoice_line.delete(
    invoice.id, 
    [
        invoice.id
    ])

Invoice Payment

20. Create an invoice payment

invoice_payment = config.invoice_payment.create(
  [
      {
          "invoiceId": invoice.id,
          "amount":{
              "value":"11.00",
              "currency":"EUR"
          }
      }
  ])

21. Update an invoice payment

update_payment = config.invoice_payment.update(
    {
        "invoiceId": invoice.id,
        "invoiceLineId": invoice.lines[0]['id'],
        "paymentId": invoice_payment.paymentId,
        "amount":{
            "value":"21.00",
            "currency":"EUR"
        }
    })

22. Search Invoice payments

invoice_payments = config.invoice_payment.search([invoice_payment.paymentId])

# iterate through the returned list
for payment in invoices_payments:
  print(payment)

23. Delete an invoice payment

del_inv_payment = config.invoice_payment.delete(
  invoice_payment.paymentId,
  [
      invoice_with_lines.lines[0]['id']
  ])

Balances

24. Retrieve Trolley Account Balance

balances = config.balances.get_trolley_balance()

# iterate through the returned list
for balance in balances:
	print(balance)

25. Retrieve PayPal Balance

balances = config.balances.get_paypal_balance()

# iterate through the returned list
for balance in balances:
	print(balance)

26. Retrieve All Balances

balances = config.balances.get_all_balances()

# iterate through the returned list
for balance in balances:
	print(balance)

Housekeeping updates

Apart form covering new APIs, we also updated the dependencies to improve the functionality and security of the SDK:

  1. requests - ~2.13.0 -> ^2.31.0

We hope these new updates help you build Trolley integration faster and more reliably.
If you have any questions or find any bugs, please open an issue or reach out to us at developers@trolley.com
Collaborations and PRs are welcome.