Pokud potřebujete do své aplikace přidat možnost platby kartou, existuje celá řada možností, které nám s tímto problémem pomohou. Jednou z těchto možností je využít k tomu PayPal, což je asi nejznámější a nejrozšířenější služba pro realizaci online plateb. Předmětem tohoto článku není srovnání PayPalu s jinými řešeními, jeho vhodnost je nutné posoudit vzhledem k projektu, na němž jej chcete použít, avšak mezi jeho hlavní výhody patří nulové pořizovací náklady, žádné papírování (není potřeba podepisovat žádné smlouvy - službu založíte a celý platební modul zprovozníte během pár hodin), vysoká spolehlivost a možnost použití i v případě, že aplikaci používají uživatelé z různých států světa – PayPal totiž funguje skoro všude a podporuje velké množství měn.
Mezi hlavní nevýhody patří vyšší poplatky za uskutečněné transakce (v době psaní článku je to cca 10 Kč + 4% z každé transakce; platební brána ve spolupráci s nějakým podnikatelským účtem u banky umí dát lepší podmínky, ale typicky platíte nemalé peníze za zřízení této služby) a dále je spousta českých uživatelů, kteří se PayPalu bojí anebo si mylně myslí, že pro zaplacení je nutné si vytvářet na PayPalu účet – tak tomu není. Vhodnost musíte posoudit sami, každopádně existuje nemalé množství aplikací, kde to má smysl.
Možnosti integrace
PayPal funguje již řadu let a nabízí různé možnosti integrace. Od jednoduchých nástrojů pro “garážové e-shopy”, kde do stránky jen vložíte kousek HTML (tzv. Payment Button), přes klasické API, až po nové moderní REST rozhraní, které si ukážeme a které umožňuje mnohem víc než jen poslat jednorázově peníze z jednoho bodu do druhého.
V tomto článku si ukážeme, jak udělat jednorázovou platbu pomocí REST API. Workflow bude následující:
1) Uživatel v naší webové aplikaci klikne na tlačítko Zaplatit.
2) Naše aplikace řekne PayPalu, kolik se bude platit a jaké položky má uživatel v košíku (s možností nastavení pokročilých parametrů, jako je cena za poštovné, daň, speciální poplatky atd.). Nastavíme současně URL, kam nás má PayPal vrátit v případě, že uživatel platbu schválí nebo zamítne (na každou z těchto alternativ je možné zvolit jinou adresu).
3) Přesměrujeme uživatele na URL, kterou nám PayPal vrátil – na této stránce uživatel platbu schválí (buď se přihlásí svým účtem, nebo zadá číslo platební karty a adresu) a po dokončení PayPal uživatele přesměruje na adresu, kterou jsme si předtím zvolili.
Mimo to nám PayPal vrátil ID transakce, to si musíme někam uložit, budeme jej potřebovat.
4a) Pokud uživatel platbu zamítnul, nemusíme dělat nic, záleží na konkrétní aplikaci, jak se k tomu postaví. Každopádně počítejte s tím, že uživatel může zavřít prohlížeč, když bude na webu PayPalu, a tím pádem se o nedokončení transakce vaše aplikace vůbec nedozví. Každou transakci, kterou vystavíte, tedy defaultně považujte za nezaplacenou.
4b) Pokud uživatel platbu schválil, přes API PayPalu ji musíme dokončit. PayPal nám do URL podstrčí parametr PayerID, který identifikuje plátce – ten musíme poslat zpátky spolu s ID transakce, které jsme získali v kroku 3.
Jedna z komplikací je, že si v kroku 3 musíme uložit ID transakce, protože jej budeme v kroku 4b potřebovat. V článku jej uložím do session, což sice funguje, ale není to nejšťastnější – může to dělat problémy, když uživatel má otevřenou tu samou stránku ve více záložkách prohlížeče (session může být sdílená mezi těmito záložkami, takže si ji mohou navzájem přepisovat, pokud by uživatel dělal dvě platby najednou apod.) a pokud session ukládáte jen v paměti, což je výchozí nastavení, tak se může kdykoliv ztratit; další kapitolou jsou serverové farmy, kde je session ještě více problematická.
Proto doporučuji každou vystavenou platbu uložit v kroku 3 do databáze a do URL, kam přesměrovává PayPal po schválení platby, si dát do query stringu nějaké vaše interní id platby (např. primární klíč z databáze). V kroku 4b pak v URL toto id budete mít, a tak můžete v databázi příslušnou platbu dohledat a pokud se ji podaří na PayPalu dokončit, označíte ji jako zaplacenou.
Krok 1 – Založení účtu
Abyste mohli pomocí PayPalu přijímat peníze, je nutné založit si Business účet. Pro potřeby jednoduchých plateb stačí varianta Standard, která je zdarma.
- Zaregistrujte se tedy na https://www.paypal.com/us/webapps/mpp/merchant.
- Po dokončení registrace účtu se přihlaste na adrese http://developer.paypal.com a klikněte na záložku Applications.
- V levém horním rohu je tlačítko Create App – tím založte novou aplikaci. Stačí vyplnit název a potvrdit. Jakmile aplikaci založíte a rozkliknete, uvidíte mimo několika možností nastavení i dvě důležité hodnoty – client id a client secret. Tyto hodnoty slouží k tomu, aby se vaše aplikace mohla k REST API přihlásit, dobře je chraňte, aby nedošlo ke kompromitaci účtu.
- Pokud si chcete nejdříve placení vyzkoušet aniž byste používali reálnou kreditní kartu, doporučuju založit sandbox Business účet na http://www.sandbox.paypal.com (doporučuju otevřít v jiném prohlížeči, nebo v takovém tom anonymním módu, abyste mohli být přihlášeni dvěma účty zároveň). Na záložce Sandbox accounts pak můžete spárovat testovací účet s tím reálným, takže se pro aplikaci vygeneruje ještě testovací client id a client secret.
- Pokud budete dělat sandboxový účet, pak si vytvořte rovnou dva – jeden Business a druhý Customer, abyste mohli sandboxovým customer accountem platbu otestovat – přistane v tom business accountu. Doporučuji testovat ve více prohlížečích nebo v tom anonymním módu, aby se jednotlivá přihlášení nehádala.
Krok 2 – Přidání SDK do projektu
Ve Visual Studiu v okně Solution Explorer klikněte na název projektu a vyberte Manage NuGet Packages. Ujistěte se, že jste v sekci online a do hledacího okna napište “PayPal REST API”. Následně do projektu nainstalujte příslušný balíček:
NuGet stáhne a přidá do projektu příslušné knihovny.
Krok 3 – Stránka s tlačítkem Zaplatit
Nyní do stránky, na níž má uživatel zaplatit, přidejte tlačítko, a do obsluhy události Click přidejte následující kód.
protected void PayButton_OnClick(object sender, EventArgs e)
{
// vygenerovat naše interní ID transakce
var internalTransactionId = Guid.NewGuid();
// vytvořit objekt platby
var payment = new Payment()
{
intent = "sale",
payer = new Payer()
{
payment_method = "paypal"
},
redirect_urls = new RedirectUrls()
{
// URL, kam se přesměruje, pokud zákazník platbu zamítne
cancel_url = PayPalManager.ApplicationRootAbsoluteUrl + "PaymentCanceled.aspx",
// URL, kam se přesměruje, pokud zákazník platbu přijme
return_url = PayPalManager.ApplicationRootAbsoluteUrl + "PaymentFinished.aspx?transaction=" + internalTransactionId
},
transactions = new List<Transaction>()
{
// seznam jednotlivých transakcí
new Transaction()
{
amount = new Amount()
{
currency = "USD", // měna
total = "10", // celková částka
details = new Details()
{
fee = "0", // zvláštní poplatky
shipping = "0", // poštovné
tax = "0", // daň
subtotal = "10" // mezisoučet
}
},
description = "Testovací platba", // popis transakce
item_list = new ItemList()
{
// seznam zakoupených produktů (obsah košíku)
items = new List<Item>()
{
new Item()
{
currency = "USD",
name = "Položka 1",
price = "10",
quantity = "1"
}
}
}
}
}
};
// vytvořit API context
Payment result;
try
{
// zaslat platbu ke zpracování PayPalu
var context = PayPalManager.CreateContext();
result = payment.Create(context);
}
catch (Exception ex)
{
// TODO: ošetřit chybu při komunikaci s PayPalem
return;
}
// TODO: v tomto okamžiku již známe ID transakce na PayPalu,
// to si musíme uložit (ideálně do databáze), budeme jej potřebovat
var paypalTransactionId = result.id;
Session["Payment-" + internalTransactionId] = paypalTransactionId;
// POZOR! Session se používá jen pro účely dema, není spolehlivá - lepší je uložit transakci do DB
// přesměrovat uživatele na stránku PayPalu, kde platbu schválí
var approveUrl = result.links.Single(l => l.rel == "approval_url").href;
Response.Redirect(approveUrl);
}
Na začátku si vygenerujeme unikátní ID platby. Jedná se o naše interní ID, doporučuji v databázi vytvořit něco jako tabulku Payments s následujcími sloupci:
- Id typu UNIQUEIDENTIFIER PRIMARY KEY
- PayPalTransactionId NVARCHAR(100) NOT NULL
- a dalšími políčky, jako je třeba OrderId, UserId apod. – zkrátka vše, co se k té platbě váže.
Dále vytvoříme objekt Payment a naplníme jeho vlastnosti – pozornost věnujte parametrům return_url a cancel_url – to jsou adresy, kam budeme přesměrováni, pokud uživatel platbu přijme resp. zamítne. Velmi důležité je do query stringu v return_url umístit naše interní ID transakce.
A dále pak transactions, což je seznam transakcí. Každá transakce může mít více položek, typicky budete mít pouze jednu transakci, v případě e-shopu pak položky z košíku překopírujete do její kolekce item_list.
U každé položky můžeme specifikovat název, měnu, jednotkovou cenu a množství. K celé transakci pak nastavíme celkovou cenu a případně tax, shipping a fee, což je daň, poštovné a další poplatky.
Jakmile máme objekt Payment nachystán, musíme jej metodou Create odeslat. Pro komunikaci s PayPal API používáme objekt ApiContext, o kterém si něco řekneme později.
Po založení platby na PayPalu se nám v proměnné result.id objeví ID transakce na PayPalu – to si musíme uložit do databáze spolu s naším interním ID – jakmile nás PayPal vrátí na cílovou adresu, podle našeho interního ID platby v adrese dohledáme ID transakce na PayPalu.
Dále je v odpovědi result adresa, kam máme uživatele přesměrovat, aby mohl schválit platbu – je v poli links a poznáme ji podle toho, že její vlastnost rel je rovna hodnotě “approval_url”.
To je první část. Zákazník je přesměrován na stránku PayPalu, kde se buď přihlásí, nebo zadá číslo karty a pár údajů a platbu potvrdí.
Krok 4 – stránka pro dokončení platby
Přidejte do projektu stránku PaymentFinished.aspx. Na tuto stránku nás PayPal přesměruje, pokud vše proběhne v pořádku a platba je schválena zákazníkem. V URL najdeme dva parametry:
- transaction je naše interní ID platby, které jsme si tam dali sami – podle něj musíme v databázi dohledat ID transakce na PayPalu – z bezpečnostních důvodů nám ho PayPal sem neposílá.
- PayerID je ID plátce, které musíme PayPalu poslat také – identifikuje to osobu, která platbu provedla.
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
// získat ID uživatele
var payerId = Request.QueryString["PayerID"];
var internalTransactionId = Request.QueryString["transaction"];
var paypalTransactionId = (string)Session["Payment-" + internalTransactionId];
try
{
// dokončit platbu
var context = PayPalManager.CreateContext();
var payment = new Payment() { id = paypalTransactionId };
var result = payment.Execute(context, new PaymentExecution() { payer_id = payerId });
// TODO: uložit do databáze, že platba proběhla úspěšně
TransactionIdLabel.InnerText = result.id;
}
catch (Exception ex)
{
// TODO: ošetřit chybu
}
}
}
V kódu nejdříve zjistíme z URL hodnoty výše uvedených dvou parametrů. Dále v databázi (v příkladu používáme session) najdeme podle interního ID platby ID transakce na PayPalu.
Dále vytvoříme objekt Payment, stačí mu nastavit ID transakce z PayPalu do vlastnosti id a zavolat funkci Execute, kam předáme ApiContext a id plátce v objektu PaymentExecution.
Pokud metoda nevyhodí výjimku, platba se provedla a můžeme si do databáze uložit, že je zaplaceno.
Nezapomeňte ošetřit případné chyby, kdyby se např. komunikace s PayPalem nepovedla kvůli nějakému výpadku konektivity nebo tak něco.
Dále vytvořte stránku PaymentCanceled.aspx, kde budou instrukce, co dělat v případě, že se zákazník rozhodnul platbu zamítnout – např. možnost vrátit se do košíku a upravit množství položek nebo zvolit jiný způsob platby atd.
Krok 5 – ApiContext
Posledním krokem k rozchození celé této záležitosti je nastavení objektu ApiContext, který slouží pro komunikaci s API a který používají funkce Payment.Create a Payment.Execute.
public class PayPalManager
{
public static bool IsSandboxMode
{
get { return Convert.ToBoolean(ConfigurationManager.AppSettings["PayPal.IsSandboxMode"]); }
}
public static string ClientId
{
get { return ConfigurationManager.AppSettings["PayPal.ClientId"]; }
}
public static string ClientSecret
{
get { return ConfigurationManager.AppSettings["PayPal.ClientSecret"]; }
}
///
/// Absolutní URL, kde běží aplikace, např. www.muj-super-obchod.cz
///
public static string ApplicationRootAbsoluteUrl
{
get
{
// uříznout doménu z URL aktuálního requestu
var url = new UriBuilder(HttpContext.Current.Request.Url);
url.Query = "";
url.Path = "";
return url.ToString();
}
}
public static APIContext CreateContext()
{
// konfigurace
var configuration = new Dictionary<string, string>();
if (IsSandboxMode)
{
configuration["mode"] = "sandbox";
}
// vytvořit token
var token = new OAuthTokenCredential(ClientId, ClientSecret, configuration).GetAccessToken();
// vytvořit kontext
return new APIContext(token) { Config = configuration };
}
}
Přidejte do projektu výše uvedenou třídu – obsahuje funkci CreateContext, která vytvoří objekt ApiContext a přihlásí se k API pomocí objektu OAuthTokenCredential. Jsou zde také vlastnosti IsSandboxMode, ClientId a ClientSecret, které načítají příslušná nastavení ze souboru web.config ze sekce appSettings.
<appSettings>
<add key="PayPal.IsSandboxMode" value="True"/>
<add key="PayPal.ClientId" value="sandboxové client id"/>
<add key="PayPal.ClientSecret" value="sandboxové client secret"/>
</appSettings>
Hodnoty client id a secret zjistíme po rozkliknutí detailu aplikace na webu http://developer.paypal.com.
Abyste viděli testovací údaje, musíte mít spárovaný reálný a sandboxový business účet, což se dělá na záložce Sandbox accounts.
V praxi doporučuji udělat si Web Config Transformaci, která zajistí, že pro lokální vývoj ve VS budete používat testovací ID a při vypublikování aplikace přes menu Publish se použijí ostré hodnoty. Stačí do souboru web.release.config může vypadat takto:
<appSettings>
<add key="PayPal.IsSandboxMode" value="False" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
<add key="PayPal.ClientId" value="ostré client id" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
<add key="PayPal.ClientSecret" value="ostré client secret" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
</appSettings>
Další možnosti
To, co jsme si zde ukázali, je jen malá část toho, co REST API umí. Můžete pomocí něj provádět například:
- Refundace (vrácení peněz)
- Stahování seznamu dokončených transakcí, vyhledávání, filtrování atd.
- Platbu přímo pomocí čísla karty a údajů, které uživatel zadá do vaší aplikace, možnost uložení těchto údajů na straně PayPalu, abyste je nemuseli ukládat sami ve své databázi a nést všechna rizika, a jejich opětovné použití v budoucnu.
To se hodí se např. pro implementaci předplatných – uživatel zadá platební údaje do vaší aplikace, vy je uložíte jen na PayPal a pak jimi můžete platit.
Nedá se takto samozřejmě dostat ke kartě, kterou si uživatel na PayPal uložil sám - z bezpečnostních důvodů je možné vidět jen karty, které se do PayPal dostaly přes vaši aplikaci, a jiné aplikace je pochopitelně nevidí.
- Přihlašování na web pomocí PayPal účtů a mnoho dalšího.