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:
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 [email protected]
Collaborations and PRs are welcome.