mitodl / mitxonline

BSD 3-Clause "New" or "Revised" License
4 stars 2 forks source link

Cybersource refund request returned ApiException: (400) when refunding a PayPal purchase #1047

Open sentry-io[bot] opened 2 years ago

sentry-io[bot] commented 2 years ago

Sentry Issue: MITXONLINE-2DA

ApiException: (400)
Reason: Bad Request
HTTP response headers: HTTPHeaderDict({'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '-1', 'Strict-Transport-Security': 'max-age=31536000', 'Content-Type': 'application/json', 'Content-Length': '251', 'x-response-time': '127ms', 'X-OPNET-Transaction-Trace': '729deeac-a3f2-4eff-98d5-29e194319572', 'Connection': 'keep-alive', 'v-c-correlation-id': '3ac554f6-0061-4bfc-aa4f-937f2010cb26'})
HTTP response body: {"id":"6639507207906418804983",...
(13 additional frame(s) were not displayed)
...
  File "CyberSource/api_client.py", line 499, in call_api
    return self.__call_api(resource_path, method,
  File "CyberSource/api_client.py", line 293, in __call_api
    response_data = self.request(method, url,
  File "CyberSource/api_client.py", line 542, in request
    return self.rest_client.POST(url,
  File "CyberSource/rest.py", line 281, in POST
    return self.request("POST", url,
  File "CyberSource/rest.py", line 243, in request
    raise ApiException(http_resp=r)

Error processing row from google sheets
arslanashraf7 commented 2 years ago

Update:

I wasn't able to get through the order completion using Paypal test account locally or on RC, I get Your order was declined. Please choose another payment method. I need to check how I can do a successful transaction using Paypal.

The Cybersource is complaining about invalid field data as seen

image

On the other hand, While I was comparing the data, the only difference I see between a regular refund locally and the refund payload in this issue is that the amount is being sent as a string in this issue whereas while testing locally I could see the amount is a float. This could be a reason for this error since CyberSource is complaining about field data being invalid.

The other thing is, I was testing this through the Refund button in Django Admin, while this issue was raised through the refund sheet.

Some of the things we might need to compare are how the amount is mentioned in the sheet, And what Successful payment response data we had in Fulfilled Orders in Django Admin.

arslanashraf7 commented 2 years ago

Please disregard the part in my above comment where I mentioned the issue might be the amount.

The issue is related to the Transaction Id, Where Cybersource is complaining about the transaction id being invalid and this one of the reasons this could happen is that the transaction id we are sending for the refund request doesn't exist on Cybersource and this I was able to reproduce locally too by sending a non-existent transaction id in refunds.

The things I'd suggest verifying are:

  1. Check if there is a transaction with transaction id 6637068427226920904262?
  2. Since this was produced by sheets where we use reference_number and then we get the transaction id from the transaction data we gather in the payment success, in the logs the transaction data is she doesn't have the transaction_id transaction

Based on point#2, We might also want to see the associated transaction objects with this order in Django Admin. There should ideally be one transaction object having transaction_id in the data.

@pdpinch Could you try to refund this order through Django Admin, Just to check if there is any issue with the refunds through the sheets flow.

pdpinch commented 2 years ago

I tried through the django admin and got another 500 error:

https://sentry.io/organizations/mit-office-of-digital-learning/issues/3632055622/?project=5864687&query=is%3Aunresolved

I also tried making a PayPal order in RC. I was surprised to find that it points to sandbox.paypal.com and not the live service. Unfortunately, I go the same error as you did.

arslanashraf7 commented 2 years ago

Hmm, This looks like the same issue, The transaction_id being sent for refund is invalid as per CyberSource. Could we check on Cybersource dashboard production and verify that there is a transaction entry there with id 6637068427226920904262?

pdpinch commented 2 years ago

I think so:

image

Let me know if there's more information that would be helpful. Or we can Zoom Friday morning.

arslanashraf7 commented 2 years ago

I think a Zoom call might be helpful to check if we can see any specific logs that might be coming from Paypal or if there is something specific wrong to this transaction only, At this point, seeing the logs, CyberSource seems to be complaining about the transaction_id which I assumed might not be present in Cybersource transactions but it is there. So there might be something else blocking the refund for a Paypal transaction. If possible, We might also want to fix Paypal transaction in RC to reproduce this error locally.

pdpinch commented 1 year ago

I put through a PayPal refund through the EBC manually, and I noticed that I had to enter the refund amount. Are we passing the refund amount through the API?

arslanashraf7 commented 1 year ago

I put through a PayPal refund through the EBC manually, and I noticed that I had to enter the refund amount. Are we passing the refund amount through the API?

Yes, The refund implementation does that and it's flexible in two ways. (Reference)

  1. We can provide a custom amount (e.g. In case of refund through Django Admin we can add a custom amount) or leave it to default
  2. If no custom amount is provided, The default amount is passed. (The default amount in this case is the value of req_amount field from the payment transaction response)

There might be another thing to look for while refunding through EBC, We need to reflect that in our database too just like the implementation does that, e.g. Ideally we should:

  1. Mark the Order as refunded
  2. (Maybe?) Add a refund transaction object for that order
  3. See, If we want to unenroll the user or not