Dev (Back & Front)ARTIGO

Implementação de Market Place em C#

Nesse tutorial veremos como implementar a operação Pay utilizando C#, criando uma biblioteca para ser utilizada por nossas aplicações.

No tutorial Implementação de Market Place com Adaptive Payments falamos sobre a operação Pay para definição de pagamentos paralelos ou encadeados.

Nesse tutorial veremos como implementar a operação Pay utilizando C#, criando uma biblioteca para ser utilizada por nossas aplicações.

Como vamos utilizar a inteface NVP, vamos trabalhar com a classe System.Collections.Specialized.NameValueCollection que oferece todos os recursos necessários para o que precisamos:

namespace PayPal {

    using System;     using System.Collections.Specialized;     using System.IO;     using System.Net;     using System.Text;     using System.Web;     public class AdaptivePayments {         public const string HOST = "svcs.paypal.com";         public const string SANDBOX_APPID = "APP-80W284485P519543T";         public const string SANDBOX_HOST = "svcs.sandbox.paypal.com";         public string UserId { get; set; }         public string Password { get; set; }         public string Signature { get; set; }         public string AppID { get; set; }         public bool Sandbox { get; set; }         public AdaptivePayments( string appID , string userId , string password , string signature ) {             AppID = appID;             UserId = userId;             Password = password;             Signature = signature;             Sandbox = appID.Equals( SANDBOX_APPID );         }         public AdaptivePayments( string userId , string password , string signature ):         this(SANDBOX_APPID,userId,password,signature) {}         public NameValueCollection execute( AdaptivePaymentsOperation o ) {             StringBuilder sb = new StringBuilder( "https://" );             sb.Append( Sandbox ? SANDBOX_HOST : HOST );             sb.Append( "/AdaptivePayments/" );             sb.Append( o.getOperation() );             Console.WriteLine( sb.ToString() );             HttpWebRequest request = (HttpWebRequest) WebRequest.Create( sb.ToString() );             request.Method = "POST";             request.ContentType = "application/x-www-form-urlencoded";             /**              * Definindo as credenciais da API              */             request.Headers.Add( "X-PAYPAL-SECURITY-USERID" , UserId );             request.Headers.Add( "X-PAYPAL-SECURITY-PASSWORD" , Password );             request.Headers.Add( "X-PAYPAL-SECURITY-SIGNATURE" , Signature );             request.Headers.Add( "X-PAYPAL-APPLICATION-ID" , AppID );             /**              * Definindo o formato da requisição e resposta              */             request.Headers.Add( "X-PAYPAL-REQUEST-DATA-FORMAT" , "NV" );             request.Headers.Add( "X-PAYPAL-RESPONSE-DATA-FORMAT" , "NV" );             /**              * Montando e enviando a requisição              */             NameValueCollection requestNvp = o.getNvp();             using ( Stream stream = request.GetRequestStream() ) {                 sb = new StringBuilder();                 for ( int i = 0 , t = requestNvp.Count ; i < t ; ++i ) {                     string key = requestNvp.GetKey( i );                     string value = requestNvp.Get( key );                     Console.WriteLine( key + "=" + value );                     sb.Append( key + "=" );                     sb.Append( HttpUtility.UrlEncode( value ) + "&" );                 }                 sb.Append( "requestEnvelope.errorLanguage=en_US" );                 UTF8Encoding encoding = new UTF8Encoding();                 byte[] bytes = encoding.GetBytes( sb.ToString() );                 stream.Write( bytes , 0 , bytes.Length );             }             /**              * Recuperando a resposta e obtendo os pares nvp              */             HttpWebResponse response = (HttpWebResponse) request.GetResponse();             NameValueCollection responseNvp;             using ( Stream stream = response.GetResponseStream() ) {                 using ( StreamReader reader = new StreamReader( stream , Encoding.UTF8 ) ) {                     string result = reader.ReadToEnd();                     responseNvp = HttpUtility.ParseQueryString( result );                 }             }             return responseNvp;         }         public PayOperation Pay() {             return Pay( ActionType.PAY );         }         public PayOperation Pay( ActionType actionType ) {             PayOperation payOperation = new PayOperation( this );             payOperation.actionType = actionType;             return payOperation;         }     } }

A definição da classe AdaptivePayments além de fazer a requisição ao PayPal, também oferece a criação da instância da classe PayOperation, que será utilizada para configurar a requisição.

namespace PayPal {

    using System;     using System.Collections.Specialized;     public abstract class AdaptivePaymentsOperation {         private AdaptivePayments ap;         protected NameValueCollection nvp;         public AdaptivePaymentsOperation ( AdaptivePayments ap ) {             nvp = new NameValueCollection();             this.ap = ap;         }         public NameValueCollection execute() {             return ap.execute( this );         }         internal NameValueCollection getNvp() {             return nvp;         }         internal abstract string getOperation();     } } A classe AdaptivePaymentsOperation oferece uma interface comum para as operações da API Adaptive Payments, qualquer classe de operação será derivada da AdaptivePaymentsOperation, como a PayOperation: namespace PayPal {

    using System;     using System.Collections.Generic;     public class PayOperation : AdaptivePaymentsOperation {         private List<Receiver> receivers;         public PayOperation ( AdaptivePayments ap ) :base( ap ) {             receivers = new List<Receiver>();             currencyCode = CurrencyCode.DEFAULT;         }         override internal string getOperation() {             return "Pay";         }         public ActionType actionType {             get { return (ActionType) getNvp().Get( "actionType" ); }             set { getNvp().Set( "actionType" , value.ToString() ); }         }         public string cancelUrl {             get { return getNvp().Get( "cancelUrl" ); }             set { getNvp().Set( "cancelUrl" , value ); }         }         public CurrencyCode currencyCode {             get { return (CurrencyCode) getNvp().Get( "currencyCode" ); }             set { getNvp().Set( "currencyCode" , value.ToString() ); }         }           public Receiver receiverList( int n ) {             if ( receivers.Count == n ) {                 receivers.Add( new Receiver(this,n) );             }             return receivers[n];         }         public string returnUrl {             get { return getNvp().Get( "returnUrl" ); }             set { getNvp().Set( "returnUrl" , value ); }         }     } }

Como podemos ver, a classe PayOperation é bastante simples, ela apenas define uma interface para que possamos definir os valores dos campos da requisição. Ela também utiliza 3 outros participantes CurrencyCode, ActionType e Receiver.

ActionType e CurrencyCode foram criados apenas para garantir que os valores informados nesses campos sejam o que é esperado pela documentação da API, facilitando a depuração e manutenção do código.

namespace PayPal {

    using System;     using System.Collections.Generic;     public class ActionType {         private static readonly Dictionary<string, ActionType> actionType = new Dictionary<string, ActionType>();         public static readonly ActionType PAY = new ActionType( "PAY" );         public static readonly ActionType CREATE = new ActionType( "CREATE" );         public static readonly ActionType PAY_PRIMARY = new ActionType( "PAY_PRIMARY" );         private string value;         private ActionType( string type ) {             actionType["PAY"] = PAY;             actionType["CREATE"] = CREATE;             actionType["PAY_PRIMARY"] = PAY_PRIMARY;             value = type;         }         public static explicit operator ActionType( string value ) {             ActionType result;             if ( actionType.TryGetValue( value , out result ) ) {                 return result;             } else {                 throw new InvalidCastException();             }         }         public override string ToString () {              return value;         }     } } namespace PayPal {

    using System;     using System.Collections.Generic;     public sealed class CurrencyCode {         private static readonly Dictionary<string, CurrencyCode> currencyCodes = new Dictionary<string, CurrencyCode>();         public static readonly CurrencyCode AUSTRALIAN_DOLLAR = new CurrencyCode( "AUD");         public static readonly CurrencyCode BRAZILIAN_REAL = new CurrencyCode( "BRL" );         public static readonly CurrencyCode CANADIAN_DOLLAR = new CurrencyCode( "CAD" );         public static readonly CurrencyCode CZECH_KORUNA = new CurrencyCode( "CZK" );         public static readonly CurrencyCode DANISH_KRONE = new CurrencyCode( "DKK" );         public static readonly CurrencyCode EURO = new CurrencyCode( "EUR" );         public static readonly CurrencyCode HONG_KONG_DOLLAR = new CurrencyCode( "HKD" );         public static readonly CurrencyCode HUNGARIAN_FORINT = new CurrencyCode( "HUF" );         public static readonly CurrencyCode ISRAELI_NEW_SHEQEL = new CurrencyCode( "ILS" );         public static readonly CurrencyCode JAPAN_YEN = new CurrencyCode( "JPY" );         public static readonly CurrencyCode MALAYSIAN_RINGGIT = new CurrencyCode( "MYR" );         public static readonly CurrencyCode MEXICAN_PESO = new CurrencyCode( "MXN" );         public static readonly CurrencyCode NORWEGIAN_KRONE = new CurrencyCode( "NOK" );         public static readonly CurrencyCode NEW_ZEALAND_DOLLAR = new CurrencyCode( "NZD" );         public static readonly CurrencyCode PHILIPPINE_PESO = new CurrencyCode( "PHP" );         public static readonly CurrencyCode POLISH_ZLOTY = new CurrencyCode( "PLN" );         public static readonly CurrencyCode POUND_STERLING = new CurrencyCode( "GBP" );         public static readonly CurrencyCode SINGAPORE_DOLLAR = new CurrencyCode( "SGD" );         public static readonly CurrencyCode SWEDISH_KRONA = new CurrencyCode( "SEK" );         public static readonly CurrencyCode SWISS_FRANC = new CurrencyCode( "CHF" );         public static readonly CurrencyCode TAIWAN_NEW_DOLLAR = new CurrencyCode( "TWD" );         public static readonly CurrencyCode TAI_BAHT = new CurrencyCode( "THB" );         public static readonly CurrencyCode TURKISH_LIRA = new CurrencyCode( "TRY" );         public static readonly CurrencyCode US_DOLLAR = new CurrencyCode( "USD" );         public static readonly CurrencyCode DEFAULT = CurrencyCode.US_DOLLAR;         private string value;         private CurrencyCode ( string value ) {             currencyCodes["AUD"] = AUSTRALIAN_DOLLAR;             currencyCodes["BRL"] = BRAZILIAN_REAL;             currencyCodes["CAD"] = CANADIAN_DOLLAR;             currencyCodes["CZK"] = CZECH_KORUNA;             currencyCodes["DKK"] = DANISH_KRONE;             currencyCodes["EUR"] = EURO;             currencyCodes["HKD"] = HONG_KONG_DOLLAR;             currencyCodes["USD"] = US_DOLLAR;             this.value = value;         }         public static explicit operator CurrencyCode( string code ) {             CurrencyCode currencyCode;             if ( currencyCodes.TryGetValue( code , out currencyCode ) ) {                 return currencyCode;             } else {                 throw new InvalidCastException();             }         }         public override string ToString() {             return  value;         }     } }

Por fim, temos o Receiver, que representa um recebedor do pagamento:

namespace PayPal {

    using System;     using System.Globalization;     public class Receiver {         private int n;         private AdaptivePaymentsOperation o;         public Receiver( AdaptivePaymentsOperation o , int n ) {             this.o = o;             this.n = n;         }         public double Amount {             get {                 string amount = o.getNvp().Get( "receiverList.receiver(" + n + ").amount" );                 return Double.Parse( amount );             }             set {                 o.getNvp().Set(                     "receiverList.receiver(" + n + ").amount",                     value.ToString( CultureInfo.InvariantCulture )                 );             }         }         public string Email {             get { return o.getNvp().Get( "receiverList.receiver(" + n + ").email" ); }             set { o.getNvp().Set( "receiverList.receiver(" + n + ").email" , value ); }         }         public bool Primary {             get {                 string v = o.getNvp().Get( "receiverList.receiver(" + n + ").primary" );                 return v == "true";             }             set {                 o.getNvp().Set( "receiverList.receiver(" + n + ").primary" , value ? "true" : "false" );             }         }     } }

Utilizar a biblioteca é bastante simples:

/**

 * Cria a instância do wrapper da API Adaptive Payments  */ AdaptivePayments adaptivePayments = new AdaptivePayments(     AdaptivePayments.SANDBOX_APPID,     "usuario",     "senha",     "assinatura" ); /**  * Cria a instância da operação Pay  */ PayOperation payOperation = adaptivePayments.Pay(); /**  * Configura um pagamento paralelo  */ payOperation.cancelUrl = "http://127.0.0.1/cancel"; payOperation.returnUrl = "http://127.0.0.1/return"; payOperation.receiverList(0).Email = "neto_1306507007_biz@gmail.com"; payOperation.receiverList(0).Amount = 100; payOperation.receiverList(1).Email = "neto.j_1324471857_biz@gmail.com"; payOperation.receiverList(1).Amount = 100; /**  * Executa a operação e obtem o retorno  */ NameValueCollection nvp = payOperation.execute(); /**  * Verifica se a operação foi bem sucedida e obtem a chave de pagamento  */ if ( nvp.Get("responseEnvelope.ack") == "Success" ) {     string payKey = nvp.Get("payKey");     return payKey; } else {     throw new ApplicationException(); }

Se separarmos essa utilização em um participante específico, teremos:

namespace MarketPlace.Models {

    using System;     using System.Collections.Specialized;     using System.Net;     using System.Net.Security;     using System.Security.Cryptography.X509Certificates;     using PayPal;     public class Payment {         private PayOperation payOperation;         private int currentReceiver = 0;         private string payKey;         public Payment () {             ServicePointManager.ServerCertificateValidationCallback = Validator;             /**              * Cria a instância do wrapper da API Adaptive Payments              */             AdaptivePayments adaptivePayments = new AdaptivePayments(                 AdaptivePayments.SANDBOX_APPID,                 "usuario",                 "senha",                 "assinatura"             );             /**              * Cria a instância da operação Pay              */             payOperation = adaptivePayments.Pay();         }         public Receiver AddReceiver( string Email , double Amount ) {             return AddReceiver( Email , Amount , false );         }         public Receiver AddReceiver( string Email , double Amount , bool Primary ) {             Receiver receiver = payOperation.receiverList( currentReceiver );             receiver.Amount = Amount;             receiver.Email = Email;             receiver.Primary = Primary;             return receiver;         }         public string CancelUrl {             get { return payOperation.cancelUrl; }             set { payOperation.cancelUrl = value; }         }         public string Execute() {             /**              * Executa a operação e obtem o retorno              */             NameValueCollection nvp = payOperation.execute();             /**              * Verifica se a operação foi bem sucedida e obtem a chave de pagamento              */             if ( nvp.Get("responseEnvelope.ack") == "Success" ) {                 payKey = nvp.Get("payKey");                 return payKey;             } else {                 throw new ApplicationException();             }         }         public string RedirectUrl {             get {                 return "https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_ap-payment&paykey=" + payKey;             }         }         public string ReturnUrl {             get { return payOperation.returnUrl; }             set { payOperation.returnUrl = value; }         }         bool Validator(             object sender,             X509Certificate certificate,             X509Chain chain,             SslPolicyErrors sslPolicyErrors ) {             return true;         }     } }

Com isso, o controlador pode receber a requisição de checkout de um produto, criar a transação e direcionar o cliente para a página de pagamento do PayPal:

namespace MarketPlace.Controllers {

    using System;     using System.Collections.Specialized;     using System.Web;     using System.Web.Mvc;     using MarketPlace.Models;     [HandleError]     public class HomeController : Controller {         public ActionResult Index () {             Payment payment = new Payment();             payment.AddReceiver( "neto_1306507007_biz@gmail.com" , 200 , true );             payment.AddReceiver( "neto.j_1324471857_biz@gmail.com" , 100 );             payment.CancelUrl = "http://127.0.0.1:8080/Cancel";             payment.ReturnUrl = "http://127.0.0.1:8080/Cancel";             payment.Execute();             return Redirect( payment.RedirectUrl );         }     } }

Nota: Os códigos de exemplo são meramente ilustrativos, com o intuito de demonstrar a facilidade de integração com a API. Apesar de funcionarem segundo a proposta do tutorial, devem ser tratados como exemplos e não como código de produção.

 

é engenheiro de aplicações e trabalha com ambiente web desde 2000 em diversas linguagens, como Java e PHP, dedicando esforços ao desenvolvimento de bibliotecas reutilizáveis para a comunidade. Especialista em integração de sistemas, possui várias bibliotecas reutilizáveis publicadas como open-source para a comunidade, como biblioteca Cielo, PayPal, ECT (Correios), BuscaPé, Lomadee, Twitter, Facebook entre várias outras. É administrador do fórum iMasters e iMasters Code, onde compartilha conhecimento com a comunidade de desenvolvedores. Também é autor de cursos no iMasters PRO.

Ver perfil