11

我正在寻找一种在代码中设置 PayPal SOAP API 端点的方法,而不是在 web.config 或 app.config 中指定它。我需要从不是 web.config/app.config 的特定于环境的配置中读取和使用端点。

这可能吗?我已经在他们的 github repo 上阅读了 SDK 的一些代码,它似乎不可能,但我希望我错过了一些东西。

我正在使用适用于 .Net 的 PayPal Merchant SDK,版本 2.1.96.0。

4

4 回答 4

6

我需要能够指定 .config 文件之外的所有重要设置。我很懒所以我是这样做的:

paypalConfig = new Dictionary<string, string>();
paypalConfig.Add("apiUsername", "xxxxx");
paypalConfig.Add("apiPassword", "xxxxx");
paypalConfig.Add("apiSignature", "xxxxx");
==>paypalConfig.Add("mode","live|sandbox");

然后你需要通过再次指定凭据来调用你的方法(没有调查太多为什么这是必要的):

var service = new PayPalAPIInterfaceServiceService(paypalConfig);
var doCaptureResponse = service.DoCapture(amount, new SignatureCredential(paypalConfig["apiUsername"], paypalConfig["apiPassword"], paypalConfig["apiSignature"]));
于 2013-04-18T17:29:58.373 回答
4

答案 2:但是如果您可以编辑 PayPal 的代码,这将正常工作......

(贝宝:如果你正在阅读这篇文章,请实现这个功能!!)


编辑:这不再是必要的 - 请参阅 Simon Labrecque 的回答


因此,在花费了几个小时并围绕我可以在实时和沙盒之间切换端点的期望编写了我的应用程序之后(就像我以前直接调用 SOAP 服务时一样),我决定深入挖掘源代码并弄清楚出去。

这是我为使其正常工作所做的更改。

假设:

脚步:

  • 您将编辑 PayPal 的源代码并在本地编译 - 但只有 2 个文件:
  • 将 .web.config 中endpoint<paypal>. 推荐的安全措施!
  • PayPalAPIInterfaceServiceService.cs 从 GitHub (Merchant SDK)下载源代码
  • DefaultSOAPAPICallHandler.cs 从 GitHub下载源代码(核心 SDK)
  • 您可能需要花一点时间来确保版本与您的 nuGet 包相同。

  • 在您的项目中的文件夹PayPalMerchantSDK或类似的东西中复制这两个副本

  • 我建议重命名这两个文件以避免与 NuGet 版本混淆和冲突。我很沮丧,只是给他们SimonsPayPalAPIInterfaceServiceServiceSimonsSOAPAPICallHandler了电话——但是你想怎么打就怎么打。

SimonsSOAPAPICallHandler.cs 的更改

更改构造函数以添加一个 boolean useSandbox

注意:它必须是第一个参数,因为我们很快就会做一些搜索和替换魔法。

    public SimonsSOAPAPICallHandler(bool useSandbox, string rawPayLoad, string attributesNamespace,
        string headerString)
        : base()
    {
        this.rawPayLoad = rawPayLoad;
        this.nmespceAttributes = attributesNamespace;
        this.headElement = headerString;

        // you can get these from config if you wish but I doubt they'll ever change
        this.endpoint = useSandbox ? "https://api-3t.sandbox.paypal.com/2.0" : "https://api-3t.paypal.com/2.0";
    }

改变GetEndPoint()

    /// <summary>
    /// Returns the endpoint for the API call
    /// </summary>
    /// <returns></returns>
    public string GetEndPoint()
    {
        return this.endpoint;
    }

添加对应的成员:

    /// <summary>
    /// Endpoint
    /// </summary>
    private string endpoint;

SimonsPayPalAPIInterfaceServiceService.cs 的更改

修改构造函数添加useSandbox参数

public SimonsPayPalAPIInterfaceServiceService(bool useSandbox) 
{ 
    this.useSandbox = useSandbox; 
}

添加对应成员

    private bool useSandbox;

对此文件进行两次搜索和替换。每个人将有大约 100 个替代品

  • 替换new DefaultSOAPAPICallHandler(new SimonsSOAPAPICallHandler(useSandbox,
  • 替换DefaultSOAPAPICallHandler defaultHandlervar defaultHandler

您刚刚所做的useSandbox作为参数添加到SimonsSOAPAPICallHandler(谢天谢地实现了IAPICallPreHandler)的构造函数中,您最终会为每个方法得到这个:

 public DoExpressCheckoutPaymentResponseType DoExpressCheckoutPayment(DoExpressCheckoutPaymentReq doExpressCheckoutPaymentReq, string apiUserName)
{
    IAPICallPreHandler apiCallPreHandler = null;
    string portName = "PayPalAPIAA";
    setStandardParams(doExpressCheckoutPaymentReq.DoExpressCheckoutPaymentRequest);
    var defaultHandler = new SimonsSOAPAPICallHandler(useSandbox, doExpressCheckoutPaymentReq.ToXMLString(null, "DoExpressCheckoutPaymentReq"), null, null);
    apiCallPreHandler = new MerchantAPICallPreHandler(defaultHandler, apiUserName, getAccessToken(), getAccessTokenSecret());
    ((MerchantAPICallPreHandler) apiCallPreHandler).SDKName = SDKName;
    ((MerchantAPICallPreHandler) apiCallPreHandler).SDKVersion = SDKVersion;
    ((MerchantAPICallPreHandler) apiCallPreHandler).PortName = portName;
    string response = Call(apiCallPreHandler);
    XmlDocument xmlDocument = new XmlDocument();
    xmlDocument.LoadXml(response);
    XmlNode xmlNode = xmlDocument.SelectSingleNode("*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='DoExpressCheckoutPaymentResponse']");
    return new DoExpressCheckoutPaymentResponseType(xmlNode);

}

而已!

现在,当您调用方法时,您可以说...

bool useSandbox = true; // or false
var service = new SimonsPayPalAPIInterfaceServiceService(useSandbox);

然后正常调用方法

caller.DoExpressCheckoutPayment(pp_request, config.AccountName);

注意:它仍然会在您的配置中查找帐户名称以找到相应的密钥。显然要小心更新到更高版本的 Merchant SDK,因为您将不得不重新执行此操作

我希望有人觉得这很有用:-)

于 2013-02-21T07:42:56.640 回答
3

这是完全可行的,但是您必须通过将绑定创建为对象来对值进行硬编码。这是我为自己的项目制定的:

protected static PayPalAPIInterface GetService()
{
    return new PayPalAPIInterfaceClient(new BasicHttpBinding()
            {
                SendTimeout = new TimeSpan(0, 5, 0),
                MaxReceivedMessageSize = 200000,
                Security = new BasicHttpSecurity()
                {
                    Mode = BasicHttpSecurityMode.Transport,
                    Transport = new HttpTransportSecurity()
                                    {
                                        ClientCredentialType = HttpClientCredentialType.None,
                                        ProxyCredentialType = HttpProxyCredentialType.None,
                                    },
                    Message = new BasicHttpMessageSecurity()
                                {
                                    ClientCredentialType = BasicHttpMessageCredentialType.Certificate,
                                }
                }
            },
            new EndpointAddress(@"https://api-3t.paypal.com/2.0/")
        ).ChannelFactory.CreateChannel();
}

您可以设置更多参数 - 理论上,.config文件中的所有内容都可以在这里复制。不过,这对我有用,所以我没有采取任何进一步的措施。

还值得注意的是,这使您能够将 PayPal 调用放入库中,而不必将绑定复制到包含该库的项目的配置文件中,这就是我首先开发它的原因。


编辑:这是基本定义PayPalAPIInterfaceClient- 不保证它实际上足以使用。

public partial class PayPalAPIInterfaceClient : System.ServiceModel.ClientBase<PayPalAPIInterfaceServiceService>
{
    public PayPalAPIInterfaceClient(System.ServiceModel.Channels.Binding binding,
                                    System.ServiceModel.EndpointAddress remoteAddress) 
           : base(binding, remoteAddress) { }
}

您还需要修改之前的代码,使其返回类型为PayPalAPIInterfaceServiceService

于 2013-01-24T19:14:04.490 回答
1

Answer 1: I agree - not possible out of the box.

It really doesn't seem to be possible without compiling the code yourself and changing quite a lot. Here's the current code from GitHub for PayPalAPIInterfaceServiceService which is actually generated - probably using a T4 template. This is a massive 2489 line file with code like this for every API method.

The key thing here is IAPICallPreHandler. You'll see it's set to null and then initialized to an instance of MerchantAPICallPreHandler. There's no way to pass it in.

    public SetExpressCheckoutResponseType SetExpressCheckout(SetExpressCheckoutReq setExpressCheckoutReq, ICredential credential)
    {
        IAPICallPreHandler apiCallPreHandler = null;
        string portName = "PayPalAPIAA";
        setStandardParams(setExpressCheckoutReq.SetExpressCheckoutRequest);
        DefaultSOAPAPICallHandler defaultHandler = new DefaultSOAPAPICallHandler(setExpressCheckoutReq.ToXMLString(null, "SetExpressCheckoutReq"), null, null);
        apiCallPreHandler = new MerchantAPICallPreHandler(defaultHandler, credential);
        ((MerchantAPICallPreHandler) apiCallPreHandler).SDKName = SDKName;
        ((MerchantAPICallPreHandler) apiCallPreHandler).SDKVersion = SDKVersion;
        ((MerchantAPICallPreHandler) apiCallPreHandler).PortName = portName;
        string response = Call(apiCallPreHandler);
        XmlDocument xmlDocument = new XmlDocument();
        xmlDocument.LoadXml(response);
        XmlNode xmlNode = xmlDocument.SelectSingleNode("*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='SetExpressCheckoutResponse']");
        return new SetExpressCheckoutResponseType(xmlNode);

    }

Now let's look at the interface :

public interface IAPICallPreHandler
{
    ICredential GetCredential();
    string GetEndPoint();
    Dictionary<string, string> GetHeaderMap();
    string GetPayLoad();
}

Doh! Doesn't GetEndPoint() look kinda like what we'd want to override.

Delving deeper into the code - the instance of GetEndPoint() that is ultimately called is this one in DefaultSOAPAPICallHandler - which as you can see goes straight to the ConfigManager.

    public string GetEndPoint() 
    {
        return ConfigManager.Instance.GetProperty(BaseConstants.END_POINT);
}

ConfigManager just goes straight to web.config or app.config

  config = (SDKConfigHandler)ConfigurationManager.GetSection("paypal");

There's no hooks or anything else to change the endpoint unfortunately.

I've considered compiling it locally and fixing it, but I'd rather lose the ability to change the endpoint in code than lose the ability to one-click update the whole package.

于 2013-02-21T06:04:16.583 回答