Jak na platby pomocí PayPalu

Tomáš Herceg       30.12.2013       C#, .NET       18724 zobrazení

PayPal je asi nejznámější a celosvětově nepoužívanější řešení pro online platby. V tomto článku si ukážeme, jak používat REST API pro realizaci jednoduché platby.

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

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.

Detail aplikace na PayPalu

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.

 

 

hodnocení článku

2 bodů / 2 hlasů       Hodnotit mohou jen registrované uživatelé.

 

Mohlo by vás také zajímat

Genericita, rozhraní a dědičnost

Jazyk C# je multiparadigmatický, což v praxi znamená, že v něm můžeme dělat hodně věcí. Jak ale do sebe jednotlivá paradigma zapadají? Co se hezky doplňuje a co není vzájemně kompatibilní? V tomto článku chci popsat, jak se chová IEquatable vzhledem k dědičnosti typu T.

Hledáme .NET vývojáře (Praha, Brno, Frýdek-Místek)

Finální pozvánka na veletrh práce pro programátory Jobs Dev 2018

 

 

Nový příspěvek

 

Příspěvky zaslané pod tento článek se neobjeví hned, ale až po schválení administrátorem.

Ověření účtu

Řešili jste nějak ověření účtu? Nejsou problémy s posíláním peněz z paypalu na firemní bankovní účet?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Co myslíte ověřením účtu?

Posílat peníze z PayPalu na firemní bankovní účet je v pohodě.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

IPN

Chybí ti tam podpora pro notifikace (https://developer.paypal.com/docs/classi... - nebo on už je nějaký jiný způsob, jak se dozvědět o refundu apod. ? Osobně IPN používám i pro zpracování transakcí (místo tvojí landing page), protože kdyby během zpracování platby u paypalu měl server/db/klientovo připojení výpadek, tak to klientovi zahlásí timeout a platba se mu nepřipočte. U IPN to PayPal zkouší tak dlouho, dokud se nezdaří.

Čermi

nahlásit spamnahlásit spam 0 odpovědětodpovědět

O IPN jsem chtěl psát další článek. :-)

Ohledně refundu pak záleží, jestli ho chceš dělat z PayPalu, nebo přímo z té vlastní aplikace, protože pokud ho děláš z vlastní aplikace, tak se zase bez IPN obejdeš.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

No a jak teda řešíš výpadky když ne přes IPN (a nejen serveru, ale i třeba klientovy konektivity - nebo prostě je blbej a zavře browser ke konci - i takový jsem potkal) ? U plateb chci 100 % spolehlivost, a ne že se Azure DB rozhodne, že pár minut nepojede.

S refundama je to složitější - Češi jsou vyčuraný jak díra do sněhu a nemají problém si zaplatit nějaký kredit/subscription a pak platbu revertnout a službu dál používat. Naštěstí nejsem ten, kdo se hádá s paypalem :)

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Výpadky neřešíme, pokud se uživatel z nějakého důvodu z PayPalu nevrátí k nám a my PayPalu nepošleme Execute, tak se mu z karty nic nestrhne - ta platba se provede až když zavoláš to payment.Execute.

A jinak Azure DB jede bez výpadků, to akorát občas vyhnije nějaké spojení v connection poolu - poslední verze EF má connection resiliency, která tohle řeší za tebe.

A co se týče refundací, my to používáme na webu, kde se kupují produkty jednorázově a tyhle situace obecně řeší obsluha, u subscription by to bylo něco jiného.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Super

Na podobný článek už delší dobu čekám. Opravdu super, díky.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Super

Fakt super, díky. Něco podobného budu v nejbližší době potřebovat.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Jak na platby pomocí PayPalu

Moc pěkný článek, díky

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

Příspěvky zaslané pod tento článek se neobjeví hned, ale až po schválení administrátorem.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback