Search code examples
c#.netpaypalintegrationpaypal-rest-sdk

Everything works in Sandbox, but I get IdentityException using PayPal Live


I am integrating PayPal payments into my website using the PayPal REST API using the .NET SDK (https://www.nuget.org/packages/PayPal). My website is running as an App Service in Microsoft Azure. I have a testing slot configured to use PayPal Sandbox, and a production slot which uses the Live credentials (configuration settings are in the App Settings, and are slot-specific).

Everything works fine in Sandbox, and I am able to authenticate and get an access token using the live configuration, but when I try to process anything I get a PayPal.IdentityException (I tried Payments as well as Billing Plans and Agreements). If I look at the IdentityException Response (or Details), I get this:

{"error":"invalid_token","error_description":"The token passed in was not found in the system"}

Digging further, it seems that the ApiContext that is created has the AccessToken set, but the Config property is null. If I look at the API request (from PayPalResource.LastRequestDetails.Value) I see that even though it's using the live configuration, it's sending the request to api.sandbox.paypal.com, not the live API. This seems to be a problem with my ApiContext, which I establish as follows:

public static APIContext Authenticate()
{
    string clientId = ConfigurationManager.AppSettings["PayPalClientId"];
    string clientSecret = ConfigurationManager.AppSettings["PayPalSecret"];
    string mode = ConfigurationManager.AppSettings["PayPalMode"];

    var config = new Dictionary<string, string>();
    config.Add("mode", mode);
    config.Add("clientId", clientId);
    config.Add("clientSecret", clientSecret);

    accessToken = new OAuthTokenCredential(config).GetAccessToken();
    return new APIContext(accessToken);    // <--- problem here
}

Apart from manually constructing the Config dictionary rather than using the ConfigManager to create it automatically from a custom config section, this follows the .NET SDK documentation very closely. What am I doing wrong?


Solution

  • The problem here is that all that is passed to the APIContext constructor is the accessToken. Without having the settings in the custom config section of Web.config or App.config, APIContext defaults to using the sandbox API.

    The solution is to explicitly set the Config property using an Object Initializer:

    return new APIContext(accessToken) { Config = config };    // <---- fixed