I making an integration with paypal. I'm using REST API for java. Currently, I have a problem with pending payments. When I look up a payment (https://api.paypal.com/v1/payments/payment/{paymentId}) to sandbox environment to check the payment status the responses are different.
When I approved payment as a seller the payment is in status
approved
and sale is in status
completed
example response:
{
"id": "PAY-5YK922393D847794YKER7MUI",
"create_time": "2013-02-19T22:01:53Z",
"update_time": "2013-02-19T22:01:55Z",
"state": "approved",
"intent": "sale",
"payer": {
"payment_method": "credit_card",
"funding_instruments": [
{
"credit_card": {
"type": "mastercard",
"number": "xxxxxxxxxxxx5559",
"expire_month": "2",
"expire_year": "2018",
"first_name": "Betsy",
"last_name": "Buyer"
}
}
]
},
"transactions": [
{
"amount": {
"total": "7.47",
"currency": "USD",
"details": {
"subtotal": "7.47"
}
},
"description": "This is the payment transaction description.",
"related_resources": [
{
"sale": {
"id": "36C38912MN9658832",
"create_time": "2013-02-19T22:01:53Z",
"update_time": "2013-02-19T22:01:55Z",
"state": "completed",
"amount": {
"total": "7.47",
"currency": "USD"
},
"parent_payment": "PAY-5YK922393D847794YKER7MUI",
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/payments/sale/36C38912MN9658832",
"rel": "self",
"method": "GET"
},
{
"href": "https://api.sandbox.paypal.com/v1/payments/sale/36C38912MN9658832/refund",
"rel": "refund",
"method": "POST"
},
{
"href": "https://api.sandbox.paypal.com/v1/payments/payment/PAY-5YK922393D847794YKER7MUI",
"rel": "parent_payment",
"method": "GET"
}
]
}
}
]
}
],
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/payments/payment/PAY-5YK922393D847794YKER7MUI",
"rel": "self",
"method": "GET"
}
]
}
and sometimes payment is
pending
and the sale status in
completed
example respose:
{
"id": "PAY-5YK922393D847794YKER7MUI",
"create_time": "2013-02-19T22:01:53Z",
"update_time": "2013-02-19T22:01:55Z",
"state": "pending",
"intent": "sale",
"payer": {
"payment_method": "credit_card",
"funding_instruments": [
{
"credit_card": {
"type": "mastercard",
"number": "xxxxxxxxxxxx5559",
"expire_month": "2",
"expire_year": "2018",
"first_name": "Betsy",
"last_name": "Buyer"
}
}
]
},
"transactions": [
{
"amount": {
"total": "7.47",
"currency": "USD",
"details": {
"subtotal": "7.47"
}
},
"description": "This is the payment transaction description.",
"related_resources": [
{
"sale": {
"id": "36C38912MN9658832",
"create_time": "2013-02-19T22:01:53Z",
"update_time": "2013-02-19T22:01:55Z",
"state": "completed",
"amount": {
"total": "7.47",
"currency": "USD"
},
"parent_payment": "PAY-5YK922393D847794YKER7MUI",
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/payments/sale/36C38912MN9658832",
"rel": "self",
"method": "GET"
},
{
"href": "https://api.sandbox.paypal.com/v1/payments/sale/36C38912MN9658832/refund",
"rel": "refund",
"method": "POST"
},
{
"href": "https://api.sandbox.paypal.com/v1/payments/payment/PAY-5YK922393D847794YKER7MUI",
"rel": "parent_payment",
"method": "GET"
}
]
}
}
]
}
],
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/payments/payment/PAY-5YK922393D847794YKER7MUI",
"rel": "self",
"method": "GET"
}
]
}
Similarly, Sanbox works when I reject the transaction. Once the payment status is failed and sale status is reversed and another time is pending and reserved.
My question is when can be 100% sure about the transaction is completed or failed?
Should I check the payment status or the sale status?
Maybe this this problem is related only with sandbox not with production.
PS. This code is only the sample, do NOT analyze it.
You should ensure that that even if the state of the sale shows completed, the payment has been approved. Even after the sale status shows completed, there might be something related to the specific buyer's account which keeps the payment in a pending state. You should ensure that payment has been approved before shipping your product.
Related
I am trying to trigger CUSTOMER.DISPUTE.RESOLVED event for disputes testing in sandbox, for that i made a test payment and opened a dispute in the resolution center. To trigger dispute resolved event i wanna call settle dispute method https://developer.paypal.com/docs/api/customer-disputes/v1#disputes-actions_adjudicate.
However i am not able to do that, cuz after create dispute is in WAITING_FOR_SELLER_RESPONSE status and i allowed only to call accept claim and provide evidence.
After accept claim i am able only to call provide supporting info method, settle method is still not allowed.
So my question is how to trigger CUSTOMER.DISPUTE.RESOLVED event in sandbox and on what dispute state i need to call settle method from here https://developer.paypal.com/docs/api/customer-disputes/v1#disputes-actions_adjudicate?
Request/Response examples
Dispute details right after open
{
"dispute_id": "PP-D-40523",
"create_time": "2021-03-03T08:47:08.000Z",
"update_time": "2021-03-03T08:48:12.000Z",
"disputed_transactions": [
{
"buyer_transaction_id": "35703532VD052404F",
"seller_transaction_id": "92198049S8582070N",
"create_time": "2021-03-03T08:41:55.000Z",
"transaction_status": "COMPLETED",
"gross_amount": {
"currency_code": "USD",
"value": "29.99"
},
"buyer": {
"name": "John Doe"
},
"seller": {
"email": "sb-47flty3787626#business.example.com",
"merchant_id": "TQNC9P6W9822C",
"name": "John Doe's Test Store"
},
"items": [],
"seller_protection_eligible": true
}
],
"reason": "UNAUTHORISED",
"status": "WAITING_FOR_SELLER_RESPONSE",
"dispute_amount": {
"currency_code": "USD",
"value": "29.99"
},
"dispute_life_cycle_stage": "CHARGEBACK",
"dispute_channel": "INTERNAL",
"extensions": {
"buyer_contacted_time": "2021-02-11T17:50:48.000Z"
},
"seller_response_due_date": "2021-03-24T08:47:50.000Z",
"allowed_response_options": {
"accept_claim": {
"accept_claim_types": [
"REFUND"
]
}
},
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/customer/disputes/PP-D-40523",
"rel": "self",
"method": "GET"
},
{
"href": "https://api.sandbox.paypal.com/v1/customer/disputes/PP-D-40523/provide-evidence",
"rel": "provide_evidence",
"method": "POST"
},
{
"href": "https://api.sandbox.paypal.com/v1/customer/disputes/PP-D-40523/accept-claim",
"rel": "accept_claim",
"method": "POST"
}
]
}
After provide evidence
{
"dispute_id": "PP-D-40523",
"create_time": "2021-03-03T08:47:08.000Z",
"update_time": "2021-03-03T11:01:55.000Z",
"disputed_transactions": [
{
"buyer_transaction_id": "35703532VD052404F",
"seller_transaction_id": "92198049S8582070N",
"create_time": "2021-03-03T08:41:55.000Z",
"transaction_status": "COMPLETED",
"gross_amount": {
"currency_code": "USD",
"value": "29.99"
},
"buyer": {
"name": "John Doe"
},
"seller": {
"email": "sb-47flty3787626#business.example.com",
"merchant_id": "TQNC9P6W9822C",
"name": "John Doe's Test Store"
},
"items": [],
"seller_protection_eligible": true
}
],
"reason": "UNAUTHORISED",
"status": "UNDER_REVIEW",
"dispute_amount": {
"currency_code": "USD",
"value": "29.99"
},
"dispute_life_cycle_stage": "CHARGEBACK",
"dispute_channel": "INTERNAL",
"extensions": {
"buyer_contacted_time": "2021-02-11T17:50:48.000Z"
},
"evidences": [
{
"evidence_type": "OTHER",
"documents": [
{
"name": "ava.jpeg"
}
],
"notes": "Test",
"source": "SUBMITTED_BY_SELLER",
"date": "2021-03-03T11:01:56.000Z",
"action_info": {
"action": "PROVIDE_EVIDENCE"
}
}
],
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/customer/disputes/PP-D-40523",
"rel": "self",
"method": "GET"
},
{
"href": "https://api.sandbox.paypal.com/v1/customer/disputes/PP-D-40523/provide-supporting-info",
"rel": "provide_supporting_info",
"method": "POST"
}
]
}
Another dispute after claim
{
"dispute_id": "PP-D-40407",
"create_time": "2021-03-02T08:17:07.000Z",
"update_time": "2021-03-02T08:47:05.000Z",
"disputed_transactions": [
{
"buyer_transaction_id": "1VS7234539684123P",
"seller_transaction_id": "2S50928513331053U",
"create_time": "2021-03-02T08:09:49.000Z",
"transaction_status": "COMPLETED",
"gross_amount": {
"currency_code": "USD",
"value": "29.99"
},
"buyer": {
"name": "John Doe"
},
"seller": {
"email": "sb-47flty3787626#business.example.com",
"merchant_id": "TQNC9P6W9822C",
"name": "John Doe's Test Store"
},
"items": [],
"seller_protection_eligible": true
}
],
"reason": "UNAUTHORISED",
"status": "UNDER_REVIEW",
"dispute_amount": {
"currency_code": "USD",
"value": "29.99"
},
"dispute_life_cycle_stage": "CHARGEBACK",
"dispute_channel": "INTERNAL",
"extensions": {
"buyer_contacted_time": "2021-02-11T17:50:48.000Z"
},
"evidences": [
{
"evidence_type": "OTHER",
"documents": [],
"notes": "Refund.",
"source": "SUBMITTED_BY_SELLER",
"date": "2021-03-02T08:47:06.000Z",
"action_info": {
"action": "ACCEPT_CLAIM",
"response_option": "REFUND"
}
}
],
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/customer/disputes/PP-D-40407",
"rel": "self",
"method": "GET"
},
{
"href": "https://api.sandbox.paypal.com/v1/customer/disputes/PP-D-40407/provide-supporting-info",
"rel": "provide_supporting_info",
"method": "POST"
}
]
}
Adjudicate on dispute with evidence
Request
POST https://api.sandbox.paypal.com/v1/customer/disputes/PP-D-40523/adjudicate
{
"adjudication_outcome": "BUYER_FAVOR"
}
Response
{
"name": "VALIDATION_ERROR",
"message": "Invalid request - see details",
"debug_id": "6aadbbc916b3c",
"information_link": "https://developer.paypal.com/docs/api/customer-disputes/v1/#errors",
"details": [
{
"issue": "ACTION_NOT_ALLOWED_IN_CURRENT_DISPUTE_STATE"
}
],
"links": []
}
You have to get it to an 'UNDER_REVIEW' by PayPal state before you can trigger the adjudication one way or the other.
So if it is 'WAITING_FOR_SELLER_RESPONSE' you should provide evidence; there is a call for that.
You may also need to make an offer and deny the offer, or escalate to claim.
This tool may be useful for understanding the potential steps: https://www.paypal.com/apex/product-profile/customerDisputes/
How could I find the seller_receivable_breakdown field from Paypal v2 API?
When I capture the payment, I don't get a seller_receivable_breakdown back in the API response, which should contain the net_amount.
Is there a reason why?
API Response:
{
"create_time": "2020-05-08T18:06:08Z",
"id": "35W12417YE077383Y",
"intent": "CAPTURE",
"links": [
{
"href": "https://api.sandbox.paypal.com/v2/checkout/orders/35W12417YE077383Y",
"method": "GET",
"rel": "self",
"title": "GET"
}
],
"payer": {
"address": {
"country_code": "SG"
},
"email_address": "sb-vnmn01690912#personal.example.com",
"name": {
"given_name": "John",
"surname": "Doe"
},
"payer_id": "ZMVMLDB2Q3RNS"
},
"purchase_units": [
{
"amount": {
"currency_code": "USD",
"value": "76.00"
},
"payee": {
"email_address": "sb-dtlz4548886#business.example.com",
"merchant_id": "9D3W7QWY9MULE"
},
"payments": {
"captures": [
{
"amount": {
"currency_code": "USD",
"value": "76.00"
},
"create_time": "2020-05-08T18:06:37Z",
"final_capture": true,
"id": "1DU784512L090023U",
"links": [
{
"href": "https://api.sandbox.paypal.com/v2/payments/captures/1DU784512L090023U",
"method": "GET",
"rel": "self",
"title": "GET"
},
{
"href": "https://api.sandbox.paypal.com/v2/payments/captures/1DU784512L090023U/refund",
"method": "POST",
"rel": "refund",
"title": "POST"
},
{
"href": "https://api.sandbox.paypal.com/v2/checkout/orders/35W12417YE077383Y",
"method": "GET",
"rel": "up",
"title": "GET"
}
],
"seller_protection": {
"dispute_categories": [
"ITEM_NOT_RECEIVED",
"UNAUTHORIZED_TRANSACTION"
],
"status": "ELIGIBLE"
},
"status": "COMPLETED",
"update_time": "2020-05-08T18:06:37Z"
}
]
},
"reference_id": "default",
"shipping": {
"address": {
"address_line_1": "123 Thomson Rd.",
"admin_area_1": "SG_zip = 308123",
"admin_area_2": "Singapore",
"country_code": "SG",
"postal_code": "308123"
},
"name": {
"full_name": "Doe John"
}
}
}
],
"status": "COMPLETED",
"update_time": "2020-05-08T18:06:37Z"
}
Either it may not be there in sandbox mode, or you may need to do a get operation on the capture id.
https://developer.paypal.com/docs/api/payments/v2/#captures_get
I contacted PayPal support about this and this is their response:
Unfortunately, it looks like the capture response will not include the "seller_receivable_breakdown" if the capture is still pending. You can see that mentioned under the "seller_receivable_breakdown" variable in this documentation about the captures object. If the capture is pending or processing still, (this happens often when a customer opts for a card transaction rather than a PayPal account balance transaction), then the capture response won't show the seller_receivable_breakdown but you can call for details on the capture right after it has finished to then see that object.
PayPal may hold a "suspicious" transaction for few days and the transaction is in a "Pending" state during the hold. Some transaction might even require your personal approval. While the transaction is on hold you cannot get the seller_receivable_breakdown via API (though this data is available in the dashboard).
You can setup a WebHook to fire on "Payment capture completed" and retrieve the information there. Be ready that the WebHook might fire in couple of days after the order.
I had set up Paypal webhooks to track the recurring payments. I had set up webhook for
BILLING.SUBSCRIPTION.UPDATED
BILLING.SUBSCRIPTION.RE-ACTIVATED
BILLING.SUBSCRIPTION.RENEWED
BILLING.SUBSCRIPTION.CANCELLED
BILLING.SUBSCRIPTION.EXPIRED
BILLING.SUBSCRIPTION.SUSPENDED
BILLING.SUBSCRIPTION.PAYMENT.FAILED
BILLING.SUBSCRIPTION.ACTIVATED
I am not getting any event apart from Activate. can anyone help me to find out the hook that I need to set up to track transactions for the recurring subscription billing?
Thanks
In the webhook simulator PAYMENT.SALE.COMPLETED is not fully shown, but it is correct to use when payment for a subscription is made. It also has a subscription_id (it called billing_agreement_id for some reason).
{
"id": "WH-528005538C798144N-7H391362AP602264K",
"create_time": "2021-04-17T22:45:35.950Z",
"resource_type": "sale",
"event_type": "PAYMENT.SALE.COMPLETED",
"summary": "Payment completed for CAD 14.55 CAD",
"resource": {
"billing_agreement_id": "I-37B4TLL0FAJJ",
"amount": {
"total": "14.55",
"currency": "CAD",
"details": {
"subtotal": "14.55"
}
},
"payment_mode": "INSTANT_TRANSFER",
"update_time": "2021-04-17T22:44:02Z",
"create_time": "2021-04-17T22:44:02Z",
"protection_eligibility_type": "ITEM_NOT_RECEIVED_ELIGIBLE,UNAUTHORIZED_PAYMENT_ELIGIBLE",
"transaction_fee": {
"currency": "CAD",
"value": "0.72"
},
"protection_eligibility": "ELIGIBLE",
"links": [
{
"method": "GET",
"rel": "self",
"href": "https://api.sandbox.paypal.com/v1/payments/sale/44P499886K055384U"
},
{
"method": "POST",
"rel": "refund",
"href": "https://api.sandbox.paypal.com/v1/payments/sale/44P499886K055384U/refund"
}
],
"id": "44P499886K055384U",
"state": "completed",
"invoice_number": ""
},
"status": "PENDING",
"transmissions": [
{
"webhook_url": "https://webhook.site/7ece4e68-de87-46df-a341-5d3dc580efe6",
"transmission_id": "b02ef980-9fce-11eb-b0ec-5bc241fbaaf3",
"status": "PENDING"
}
],
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-528005538C798144N-7H391362AP602264K",
"rel": "self",
"method": "GET",
"encType": "application/json"
},
{
"href": "https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-528005538C798144N-7H391362AP602264K/resend",
"rel": "resend",
"method": "POST",
"encType": "application/json"
}
],
"event_version": "1.0"
}
See https://developer.paypal.com/docs/integration/direct/webhooks/event-names/#subscriptions
It looks like you want PAYMENT.SALE.COMPLETED
There is also one for refunded and reversed.
This is an edit to my original post. I think the paypal rest api sandbox is down.
The error just started and I haven't had it before:
here is the capture URL:
https://api.sandbox.paypal.com/v1/payments/payment/PAY-8EN83459YH6921921KJ4QNHQ
Its a 404 error. Here is the body:
{
name: "INVALID_RESOURCE_ID"
message: "The requested resource ID was not found"
information_link: "https://developer.paypal.com/webapps/developer/docs/api/#INVALID_RESOURCE_ID"
debug_id: "e56bae98dcc26"
}
The payment was properly created and I used the same credentials and the payment link provided in the returned payment creation response.
Here was the response from the original payment creation call:
{
"id": "PAY-8EN83459YH6921921KJ4QNHQ",
"create_time": "2013-11-05T14:54:22Z",
"update_time": "2013-11-05T14:54:30Z",
"state": "approved",
"intent": "authorize",
"payer": {
"payment_method": "credit_card",
"funding_instruments": [
{
"credit_card": {
"type": "visa",
"number": "xxxxxxxxxxxx0331",
"expire_month": "11",
"expire_year": "2018",
"first_name": "Joe",
"last_name": "Shopper",
"billing_address": {
"line1": "52 N Main ST",
"city": "Johnstown",
"state": "OH",
"postal_code": "43210",
"country_code": "US"
}
}
}
]
},
"transactions": [
{
"amount": {
"total": "7.47",
"currency": "USD",
"details": {
"subtotal": "7.41",
"tax": "0.03",
"shipping": "0.03"
}
},
"description": "This is the payment transaction description.",
"related_resources": [
{
"authorization": {
"id": "0UH21242UH1119007",
"create_time": "2013-11-05T14:54:22Z",
"update_time": "2013-11-05T14:54:30Z",
"state": "authorized",
"amount": {
"total": "7.47",
"currency": "USD",
"details": {
"subtotal": "7.41",
"tax": "0.03",
"shipping": "0.03"
}
},
"parent_payment": "PAY-8EN83459YH6921921KJ4QNHQ",
"valid_until": "2013-12-04T14:54:22Z",
"links": [
{
"href": "https:\/\/api.sandbox.paypal.com\/v1\/payments\/authorization\/0UH21242UH1119007",
"rel": "self",
"method": "GET"
},
{
"href": "https:\/\/api.sandbox.paypal.com\/v1\/payments\/authorization\/0UH21242UH1119007\/capture",
"rel": "capture",
"method": "POST"
},
{
"href": "https:\/\/api.sandbox.paypal.com\/v1\/payments\/authorization\/0UH21242UH1119007\/void",
"rel": "void",
"method": "POST"
},
{
"href": "https:\/\/api.sandbox.paypal.com\/v1\/payments\/payment\/PAY-8EN83459YH6921921KJ4QNHQ",
"rel": "parent_payment",
"method": "GET"
}
]
}
}
]
}
],
"links": [
{
"href": "https:\/\/api.sandbox.paypal.com\/v1\/payments\/payment\/PAY-8EN83459YH6921921KJ4QNHQ",
"rel": "self",
"method": "GET"
}
]
}
There is a reported problem at the moment with REST API. PayPal are working on it.
I was hitting my head against the wall for a while thinking that this PayPal bug was the cause of my problems. However, it turns out that PayPal generates the same error when you are using the wrong configuration in your client. In my case, I had the Android library set to use ENVIRONMENT_NO_NETWORK, and not ENVIRONMENT_SANDBOX. This produces real-looking transaction IDs, which (of course) are not registered with the server.
A few months ago I asked the following for a List of Sale Transactions:
PayPal Restful API - Pull List of Sale Transactions
In that question I got an answer referencing the List of Payment Resources call, which is great, but now I need to do something similar for refunds. However it's unclear to me from the documentation whether this includes Refunds.
Does the List of Payment Resources include Refund transactions (which would be a reverse payment of sorts), or is there some other way of accessing a list of Refund transactions?
Thanks in advance.
You currently cannot specifically get a list of refunded transactions. However, as you mention, you can get a list of payments and within that you will see the state of the sale object as refunded and a refund object with the state of completed:
Example:
{
"id": "PAY-ABCDEFGHIJKLMNO1234567890",
"create_time": "2013-09-20T15:44:04Z",
"update_time": "2013-09-20T16:10:05Z",
"state": "approved",
"intent": "sale",
"payer": {
"payment_method": "paypal",
"payer_info": {
"email": "bob-facilitator#example.com",
"first_name": "Bob",
"last_name": "Example",
"payer_id": "ABCDEFG123456"
}
},
"transactions": [
{
"amount": {
"total": "1.00",
"currency": "USD",
"details": {
"subtotal": "1.00"
}
},
"description": "example description",
"related_resources": [
{
"sale": {
"id": "ABCDEFGH123456789",
"create_time": "2013-09-20T15:44:04Z",
"update_time": "2013-09-20T16:10:05Z",
"state": "refunded",
"amount": {
"total": "1.00",
"currency": "USD"
},
"parent_payment": "PAY-ABCDEFGHIJKLMNO1234567890",
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/payments/sale/12345678ABCDEFGIJ",
"rel": "self",
"method": "GET"
},
{
"href": "https://api.sandbox.paypal.com/v1/payments/sale/12345678ABCDEFGIJ/refund",
"rel": "refund",
"method": "POST"
},
{
"href": "https://api.sandbox.paypal.com/v1/payments/payment/PAY-ABCDEFGHIJKLMNO1234567890",
"rel": "parent_payment",
"method": "GET"
}
]
}
},
{
"refund": {
"id": "ABCDEFGH123456789",
"create_time": "2013-09-20T16:10:05Z",
"update_time": "2013-09-20T16:10:05Z",
"state": "completed",
"amount": {
"total": "1.00",
"currency": "USD"
},
"sale_id": "12345678ABCDEFGIJ",
"parent_payment": "PAY-57E15446PJ712294VKI6G2RA",
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/payments/refund/ABCDEFGH123456789",
"rel": "self",
"method": "GET"
},
{
"href": "https://api.sandbox.paypal.com/v1/payments/payment/PAY-ABCDEFGHIJKLMNO1234567890",
"rel": "parent_payment",
"method": "GET"
},
{
"href": "https://api.sandbox.paypal.com/v1/payments/sale/12345678ABCDEFGIJ",
"rel": "sale",
"method": "GET"
}
]
}
}
]
}
],
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/payments/payment/PAY-ABCDEFGHIJKLMNO1234567890",
"rel": "self",
"method": "GET"
}
]
}