Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
v:2.0:webhooks [2019/11/05 13:48] – [Sending Webhooks & Automatic Retries] marija | v:2.0:webhooks [2025/06/27 16:46] (current) – [Example Payload] adam | ||
---|---|---|---|
Line 3: | Line 3: | ||
===== Overview ===== | ===== Overview ===== | ||
- | Foxy's Webhook functionality allows you to create integrations which subscribe to notifications of new transactions | + | Foxy's Webhook functionality allows you to create integrations which subscribe to notifications of events |
+ | |||
+ | We currently have three different types of Webhook endpoints you can utilise, depending on your needs: | ||
- | We currently have four different types of Webhook endpoints you can utilise, depending on your needs: | ||
* [[# | * [[# | ||
- | * [[# | ||
* [[# | * [[# | ||
* [[# | * [[# | ||
+ | ===== IP Addresses & Networking ===== | ||
+ | |||
+ | Our v2 JSON webhooks will come from the following IPs: | ||
+ | * **IP:** '' | ||
+ | * **IP:** '' | ||
+ | (Our v1 JSON webhooks are not sent from static IPs.) | ||
===== Sending Webhooks & Automatic Retries ===== | ===== Sending Webhooks & Automatic Retries ===== | ||
- | When a new transaction is placed and your webhooks are triggered, Foxy will wait for 1 minute for a response from your webhook endpoint. If a webhook fails to respond successfully (that is, if we don't receive a // | + | When webhooks are triggered |
+ | |||
+ | ===== Transaction Statuses ===== | ||
+ | |||
+ | For working with transactions with the webhook, a '' | ||
+ | |||
+ | ^ Status | ||
+ | | // | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | |||
+ | Those statuses that are marked as "// | ||
+ | |||
+ | The '' | ||
+ | |||
+ | Some gateways will trigger multiple web hooks to be sent for a single transaction, | ||
===== PayPal, Amazon Pay, and other Hosted Gateways ===== | ===== PayPal, Amazon Pay, and other Hosted Gateways ===== | ||
Line 22: | Line 53: | ||
</ | </ | ||
- | If you use any of the hosted gateways, note that every transaction may generate more than one notification. | + | If you use any of the hosted gateways |
- | Every time our IPN (Instant Payment Notification) server gets a notification from the gateway we also send you a notification. Generally, these notifications should arrive to your webhook endpoint in order, but it's possible that these webhooks are sent to your endpoint within a //very// short window (less than 0.5 seconds), and with possible network delays, your endpoint may receive the notifications out of order. Make sure you account for the '' | + | |
- | The webhook will include | + | Every time our IPN (Instant Payment Notification) server gets a notification from the gateway we also send you a notification. |
- | For instance, depending | + | The '' |
+ | |||
+ | Most hosted gateways usually send 2 notifications: | ||
Here is the current list of hosted gateways which take advantage of the status field: | Here is the current list of hosted gateways which take advantage of the status field: | ||
Line 46: | Line 78: | ||
* Mollie | * Mollie | ||
* Ogone | * Ogone | ||
+ | * PayPal Commerce Platform | ||
* PayPal Express Checkout | * PayPal Express Checkout | ||
* PayPal Plus | * PayPal Plus | ||
Line 61: | Line 94: | ||
: The title of the webhook | : The title of the webhook | ||
; '' | ; '' | ||
- | : The type of webhook - '' | + | : The type of webhook - '' |
; '' | ; '' | ||
: The error message received from the endpoint, trimmed to 500 characters. For the JSON Webhook this is the body of the response. | : The error message received from the endpoint, trimmed to 500 characters. For the JSON Webhook this is the body of the response. | ||
Line 69: | Line 102: | ||
Along with the error log, an email will also be sent to the store' | Along with the error log, an email will also be sent to the store' | ||
- | ==== Did the transaction | + | ==== Did the event complete? ==== |
- | The transaction | + | The event that triggered the webhook still completed successfully. If it was a transaction webhook, the customer was charged and you'll receive payment for the transaction as your chosen payment gateway processes it. |
+ | ==== Automatic deactivation of failing webhooks ==== | ||
+ | |||
+ | If a single webhook endpoint continues to fail 12 times in a row (whether that is all from one single trigger, or from multiple triggers for the resource happening at the same time), then to prevent unnecessary attempts, the webhook will be deactivated, | ||
+ | |||
+ | An inactive webhook can be reactivated by editing it in the administration. When reactivating an inactive webhook, any resources that would have triggered the webhook while it was inactive are not automatically sent. These will need to be manually resent to your webhook endpoint as detailed below. | ||
==== Troubleshooting Webhook Errors ==== | ==== Troubleshooting Webhook Errors ==== | ||
If at least one of your webhooks failed to handle the payload successfully - you will want to correct the error and refeed the payload to the failed endpoints. | If at least one of your webhooks failed to handle the payload successfully - you will want to correct the error and refeed the payload to the failed endpoints. | ||
- | Check the error: You can see the specific error that was triggered by your webhook by viewing the errors page of your stores FoxyCart administration. Check out this page: http:// | + | Check the error: You can see the specific error that was triggered by your webhook by viewing the errors page of your stores FoxyCart administration. |
+ | |||
+ | Check out this page: http:// | ||
===== Retrying Webhooks ===== | ===== Retrying Webhooks ===== | ||
Line 88: | Line 128: | ||
After triggering the transaction to be sent to your webhook again, the system will re-attempt the process again for another hour [[# | After triggering the transaction to be sent to your webhook again, the system will re-attempt the process again for another hour [[# | ||
+ | |||
====== JSON Webhook ====== | ====== JSON Webhook ====== | ||
The JSON webhook is, as you may have guessed, a JSON formatted payload sent to your endpoint. | The JSON webhook is, as you may have guessed, a JSON formatted payload sent to your endpoint. | ||
+ | |||
+ | The below documentation is for our latest version of our webhooks functionality (currently labelled as " | ||
+ | |||
+ | ===== Creating a webhook ===== | ||
+ | |||
+ | JSON webhooks can be created from the " | ||
+ | |||
+ | Each webhook requires a name, a URL and an encryption key (which will be automatically set with a value). Enter an identifying name and the URL to the endpoint that will be receiving the webhook payload. We'll explain how to create that endpoint in the next section. | ||
+ | |||
+ | If you'd like to provide your own encryption key for the webhook, click the "Show secret" | ||
+ | |||
+ | ==== Subscribed resources ==== | ||
+ | |||
+ | There are several different resource types that you can subscribe to for the webhooks, one resource per webhook. The following resource types are currently supported: | ||
+ | |||
+ | |||
+ | <WRAP center round info 95%> | ||
+ | Note: Once you have created a webhook, it's not supported to change the subscribed resource type. If you need to change that for a given resource, you can delete the existing webhook and create another. | ||
+ | </ | ||
+ | |||
+ | === Transactions === | ||
+ | |||
+ | Triggered whenever a transaction is completed, is modified by the API or admin, it's payment status is updated, or it is captured/ | ||
+ | |||
+ | Events: '' | ||
+ | |||
+ | === Subscriptions === | ||
+ | |||
+ | Triggered whenever a subscription resource is created or updated. Subscriptions are created when first purchased as part of a transaction, | ||
+ | |||
+ | Events: '' | ||
+ | |||
+ | Note that the '' | ||
+ | |||
+ | === Customers === | ||
+ | |||
+ | Triggered whenever a customer resource is created or updated. If a change is made to a customer who has an associated subscription and you have the subscription webhook enabled, we will send two webhooks, one for the customer and another for the subscription. | ||
+ | |||
+ | Events: '' | ||
+ | |||
+ | === Transaction Log === | ||
+ | |||
+ | Triggered whenever a transaction resource is updated in some way, causing an additional log to be added to the transaction log. This can include a transaction or transaction item being edited, captured, refunded, or a transaction being added, moved or removed from a transaction folder. | ||
+ | |||
+ | Events: '' | ||
+ | |||
+ | <WRAP center round important 95%> | ||
+ | As all events will be sent to your endpoint for the subscribed resource, you should ensure that you only process the events that you care about - for example, only the '' | ||
+ | |||
+ | You can confirm the event using the '' | ||
+ | </ | ||
+ | |||
+ | <WRAP center round tip 95%> | ||
+ | **Selling subscriptions? | ||
+ | If you do recurring billing with Foxy, please be aware that subscriptions can start in the future, resulting in a $0 initial transaction. Also note that subscription modifications can result in $0 transactions. This can cause problems if you don't account for it. (For instance, you may not want to ship product or provision access today if the subscription starts next week.) We recommend you test things out to ensure you're handling webhooks the way you want. | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | ==== API filter query string ==== | ||
+ | |||
+ | The JSON webhooks uses our API as the source of data, and by default will just return the base resource as the payload (for example, the transaction webhook will just send the [[https:// | ||
+ | |||
+ | The following are examples of query strings that you might want to use: | ||
+ | |||
+ | === Transactions === | ||
+ | |||
+ | * Provide all data related to the transaction (default)\\ < | ||
+ | * Just send the transaction ID and status\\ < | ||
+ | |||
+ | === Subscriptions === | ||
+ | |||
+ | * Provide all data related to the subscription (default)\\ < | ||
+ | |||
+ | === Customers === | ||
+ | |||
+ | * Provide all data related to the customer (default)\\ < | ||
+ | |||
+ | === Transaction Logs === | ||
+ | |||
+ | * Provide all data related to the log (default)\\ < | ||
+ | |||
+ | ==== Saving The Webhook ==== | ||
+ | |||
+ | To save changes to the webhook, click the " | ||
+ | |||
+ | Upon saving, a '' | ||
+ | |||
+ | ===== Testing Webhooks: Helpful Tips ===== | ||
+ | |||
+ | It's often helpful to see exactly what the webhook request (sent to your servers) looks like. Though we don't offer this functionality natively, there are a variety of free services that you can use to send and view webhook requests. One we like is [[https:// | ||
+ | |||
+ | ===== Receiving a webhook ===== | ||
+ | |||
+ | Once you've configured your webhook - whenever actions happen that relate to the events you're subscribed to, a JSON payload containing information about that event will be sent to your endpoint. If your endpoint is secured with HTTPS, then the data is sent unencrypted ready for you to use. If your endpoint is not secured though, the data will be encrypted using '' | ||
+ | |||
+ | ==== Headers ==== | ||
+ | |||
+ | Any requests made to your webhook' | ||
+ | |||
+ | ^ Header ^ Description ^ | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | |||
+ | ==== Example Payload ==== | ||
+ | |||
+ | The JSON payload will follow the same structure as our Hypermedia API. Most of the objects will include the API's '' | ||
+ | |||
+ | The source of the payload data is our Hypermedia API, so you can use [[https:// | ||
+ | |||
+ | <WRAP center round info 95%> | ||
+ | Webhook payloads will always reflect the most current state of the resource at the time it was sent. If a webhook is resent, it will be the current state of the resource, rather than the state of the resource at the time that it was sent. | ||
+ | </ | ||
+ | |||
+ | |||
+ | In the example payloads below - to save space, the '' | ||
+ | |||
+ | === Transaction === | ||
+ | |||
+ | <code javascript> | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | ] | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | " | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | ], | ||
+ | " | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | ], | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | ], | ||
+ | " | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | ], | ||
+ | " | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | ], | ||
+ | " | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | ], | ||
+ | " | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | ], | ||
+ | " | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | ] | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }</ | ||
+ | |||
+ | Within the payload, the following attributes can be helpful for handling the payload: | ||
+ | |||
+ | ; '' | ||
+ | : What type of transaction this was, could be '' | ||
+ | |||
+ | === Subscription === | ||
+ | |||
+ | <code javascript> | ||
+ | " | ||
+ | // Note: These two links are included in order to illustrate where you can obtain subscription-related URLs. There are many other links included in the webhook, and you can explore them in our API documentation at https:// | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | ], | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | ], | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | ], | ||
+ | " | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | ], | ||
+ | " | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | ] | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }</ | ||
+ | |||
+ | |||
+ | === Customer === | ||
+ | |||
+ | <code javascript> | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | ], | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | " | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | ] | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }</ | ||
+ | |||
+ | === Transaction Log === | ||
+ | <code javascript> | ||
+ | { | ||
+ | " | ||
+ | | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | { | ||
+ | " | ||
+ | | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | ], | ||
+ | " | ||
+ | " | ||
+ | | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | }, | ||
+ | " |