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.







