2015-02-26 41 views
7

Pracuję nad aplikacją C# VS2012 Framework 4.5 MVC, która próbuje uzyskać zgodność z PCI za pomocą Payflow Pro (https://pilot-payflowpro.paypal.com). Używamy PayflowPro od lat i właśnie tego muszę użyć. Z mojego czytania wynika, że ​​powinienem użyć Przejrzystego przekierowania, więc nie publikuję niczego prywatnego na moim serwerze internetowym, choć nie wiem, czy potrzebuję tego, w jaki sposób mam nadzieję sobie z tym poradzić. Mam też kilka pytań ...Paypal Payflow Przejrzyste przekierowanie, SecureToken z AJAX?

Jak myślę, że to wszystko działa: moim rozumieniu jest to, że trzeba securetoken (komunikacja Paypal, Trip 1). Następnie publikujesz bezpieczne dane (CC, exp, kod bezpieczeństwa), w tym securetoken (komunikacja z Paypal, trip 2) i otrzymasz autoryzację i transactionID sprzedaży.

Jak mam nadzieję to zrobić: jestem zamierzający na mające formę, która będzie miała wszystkie informacje (dane użytkownika, informacje dotyczące wysyłki oraz informacje o CC), a gdy użytkownik naciśnie przycisk kupna , Użyję AJAX do przetwarzania podróży 1 na mój serwer (nie są wysyłane żadne bezpieczne informacje o użytkowniku). Tutaj utworzę URL + parametry i wyślę paypal my un/pw info, aby pobrać token (wszystko z mojego serwera). Odpowiedź zostanie zwrócona do klienta i, jeśli się powiedzie, będę wtedy bezpośrednio komunikować się przez AJAX z serwerem Paypal Gateway, tym razem wysyłając bezpieczny token CC Info + (trip # 2). Opierając się na odpowiedzi na nr 2, dam znać użytkownikowi, co kupuje. Trip 2 nie powinien wymagać informacji o Paypal UN/PW, ponieważ mógłby być łatwo widoczny na kliencie, a ja dołączam SecureToken, który POWINIEN identyfikować oryginalną transakcję. Z tego co wyjaśniłem, nie widzę potrzeby stosowania Przejrzystego przekierowania. Czy może czegoś tutaj brakuje?

Jakiego typu transakcji chcę użyć? Utwórz "Autoryzację" dla podróży nr 1, a następnie "Sprzedaż" dla podróży nr 2?

Więc oto Maryla Rodowicz typ kodowania rzeczy: Dla mojego R & D testów buduję własną ciąg parametrów pary nazwa/wartość (patrz poniżej) i komunikuje się z serwerem bramy poprzez WebRequest poprzez ich piaskownicy/testowy URL (pilot-payflowpro.paypal.com). Dostaję pomyślną odpowiedź i ponownie otrzymuję SECURETOKEN. Wstępne żądanie (pokazane poniżej) dla bezpiecznego tokena to TRXTYPE = A (autoryzacja), nie są wysyłane informacje o karcie. Czy chcę najpierw autoryzować?

Oto moje parametry (może zawierać shipto informacji, jak również, ale to nie jest wymienione poniżej):

USER=myAuthUserName 
&VENDOR=myAuthUserName 
&PARTNER=myPartner 
&PWD=myPassword 
&AMT=21.43 
&BILLTOFIRSTNAME=FName 
&BILLTOLASTNAME=LName 
&BILLTOSTREET=123 Main Street 
&BILLTOSTREET2=Apt 203B 
&BILLTOCITY=MyCity 
&BILLTOSTATE=CA 
&BILLTOZIP=77777 
&BILLTOPHONENUM=4444444444 
&[email protected] 
&CURRENCY=USD 
**&TRXTYPE=A** 
&SILENTTRAN=TRUE 
&CREATESECURETOKEN=Y 
&SECURETOKENID=a99998afe2474b1b82c8214c0824df99 

Jak powiedziałem, mam udaną odpowiedź i przejść do następnego kroku wysyłania danych bezpiecznego (CC#, EXPDATE, kod zabezpieczający). Po usunięciu informacji o UN/PW/VENDOR/Partner z paramów otrzymuję błąd z powodu nieprawidłowego uwierzytelnienia użytkownika. Ale widząc, że dynamicznie buduję to drugie połączenie, nie mogę mieć mojego paypal un/pw. czego mi brakuje? Czy ktoś oferuje pomoc z powyższymi lub innymi pytaniami z góry?

Proszę dać mi znać, jeśli potrzebuję wyjaśnienia. Z góry dzięki za poświęcony czas!

+0

Patrząc na dokument znajdujący się tutaj: https: //developer.paypal.com/docs/classic/payflow/integration-guide/# about-the-secure-token "Serwer Gateway kojarzy Twój identyfikator z bezpiecznym tokenem i zwraca token jako ciąg maksymalnie 32 znaków alfanumerycznych To przekazać dane transakcji do hostowanej strony transakcji, przekazać bezpieczny token i bezpieczny token ID w formularzu HTTP. Token i identyfikator uruchamiają serwer Gateway, aby pobrać dane i wyświetlić je w celu zatwierdzenia przez klienta. " Używamy własnego domowego koszyka na zakupy, więc nie używam stron HOSTED, może to mój problem? – RichieMN

+0

WYDAJE, że za pomocą tej metody musisz podać informacje uwierzytelniające dla każdego żądania. Zajrzyj do interfejsu REST API Paypal. Stworzę kolejne pytanie/komentarz z tym, czego się dowiem ... – RichieMN

+0

Hmmm. Właśnie próbowałem to zaimplementować i to nie działa. Kiedy wykonuję przekierowanie do adresu URL payflowlink, odpowiada on za pomocą HTTP200 i bez treści HTML (i bez przekierowania). Rozmawiałem z obsługą techniczną systemu PayPal i twierdzą, że ta sekwencja nie jest możliwa: formularz wejściowy karty kredytowej musi być hostowany na serwerze PayPal, aby korzystać z usługi payflowlink (za pomocą ramki iFrame do osadzenia jej na naszej stronie). Zostałem skierowany do interfejsu API DoDirectPayment. Poświęcę trochę czasu na zrobienie testu. – Owen

Odpowiedz

3

Udało mi się użyć odpowiedzi RichieMN, aby uzyskać działające przekierowanie przezroczyste. Jednak problem z przekierowaniem z window.location.replace w funkcji SendCCDetailsToPaypal polega na przekazywaniu danych w łańcuchu GET.

Działa to na boku Payflow bramy, ale kiedy wysłać przeglądarkę klienta z powrotem do swojej responseURL, dzienniki Apache pokaże cały payflowlink.paypal.com URL, w tym ciąg GET jako polecający w Apache logi dostępu! Ten ciąg GET zawiera numer karty kredytowej, a teraz właśnie straciłeś zgodność ze standardem PCI!

Aby złagodzić ten problem, można umieścić SecureToken i SecureTokenID do swojej karty kredytowej formularz zgłoszeniowy, i umieścić go bezpośrednio do payflowlink.paypal.com, czy można przepisać SendCCDetailsToPaypal funkcję zbudować formularz i prześlij to coś takiego:

function SendCCDetailsToPaypal() { 
    var parameters = { 
     "SECURETOKEN": secureToken, 
     "SECURETOKENID": secureTokenID, 
     "ACCT": $("#ccNumber").val(), 
     "EXPDATE": $("#expMonth").val() + $("#expYear").val(), 
     "CSC": $("#ccSecurityCode").val() 
    }; 
    var form = $('<form></form>'); 
    form.attr("method", "post"); 
    form.attr("action", "https://pilot-payflowlink.paypal.com"); 
    $.each(parameters, function(key, value) { 
     var field = $('<input></input>'); 
     field.attr("type", "hidden"); 
     field.attr("name", key); 
     field.attr("value", value); 
     form.append(field); 
    }); 
    $(document.body).append(form); 
    form.submit(); 
} 

Ponieważ forma przesyła dane za pośrednictwem poczty, gdy serwer dostaje POST wynik nazad, wywołującej nie zawiera żadnych danych wrażliwych, a zgodność PCI jest zachowana.

+0

Fajny połów, dzięki! – RichieMN

6

Po spędzeniu kilku godzin z inżynierem Paypal, udało mi się znaleźć rozwiązanie dla Paypal Payflow Transparent Redirect bez hostowanych stron (mają własną stronę płatności). Ponownie, oto dokumentacja, która według inżyniera jest dość myląca: Payflow API Documentation. Ponadto kod nie jest zoptymalizowany, ponieważ był to tylko aplikacja R & D, ale jako całość działa dla mnie. Tylko przykład i wyjaśnienie, i jestem pewien, że istnieją lepsze sposoby robienia indywidualnych kroków. Mam nadzieję, że to pomaga i pozwala ominąć niektóre blokady dróg, które spowalniały integrację z Paypal Payflow.

TAK, jest zgodna ze standardem PCI, ponieważ żadne bezpieczne dane klientów nie trafią na własne serwery. Pamiętaj, że zgodność z PCI jest dość skomplikowana i zaangażowana, ale jest to duża część tego. Ok, więc wyjaśnię, co zrobiłem, aby wykonać tę pracę w środowisku MVC C#. Wyjaśnię tutaj kroki, a następnie dołącz poniższy kod.

  1. KLIENT: Klient kończy dodawać pozycje do koszyka i naciska przycisk KUP. JavaScript obsługuje kliknięcie przycisku, nie przesyła i przechodzi do następnego kroku.
  2. KLIENT -> SERWER: Funkcja AJAX POSTS do metody serwera, aby skontaktować się z Paypal za bezpieczny token jednorazowy. Ta komunikacja identyfikuje użytkownika (kupca) z Paypal za pomocą Twojego uwierzytelnienia, unikalnego identyfikatora transakcji (identyfikatora) i niepoufnych danych dotyczących transakcji (suma, informacje rozliczeniowe, informacje o wysyłce, szczegóły dotyczące zwrotnego adresu URL). W ten sposób wszystkie twoje dane osobowe kupca są bezpieczne (serwer internetowy do Paypal).
  3. SERWER -> KLIENT: Z powyższej transakcji otrzymasz ciąg parametrów zawierający bezpieczny token (między innymi zobacz metodę z przykładem). Korzystając z tej informacji, dynamicznie tworzę mój adres URL, który w końcu będę potrzebował na kliencie dla przezroczystej części przekierowania, i wyślę ciąg URL do klienta.
  4. KLIENT: Korzystając z adresu URL, który został zwrócony w kroku 3, uzupełniam adres URL, dodając wymagane parametry karty kredytowej za pomocą jQuery.
  5. KLIENT -> PAYPAL: Tutaj nie rozumiem, co robić. Podczas gdy krok nr 2 był post, ten krok będzie REDIRECT. Jasne, wydaje się to właściwe, ponieważ nazywa się to "przezroczystym przekierowaniem", ale ta część nie ma dla mnie sensu. Tak więc, gdy cały adres URL zostanie ukończony, dosłownie przekierujesz okno do Paypal w celu przetworzenia transakcji.
  6. PAYPAL -> SERVER: PayPal odsyła z powrotem do jednego z adresów URL zawartych w kroku 2 (do publicznej metody na jednym z moich kontrolerów), a ja odczytuję obiekt odpowiedzi i analizuję parametry.

Łatwo, prawda? Być może, ale dla mnie krok 5 sprawił mi duże problemy. Używałem POST i nie rozumiałem, dlaczego ciągle dostaję błędy w odpowiedzi. Była to strona html zawierająca informacje o nieważnym sprzedawcy lub uwierzytelnieniu. Pamiętaj, aby przekierować, a nie publikować w kroku 5.

KOD:

KROK 1: atrybut onclick przycisku wywołać funkcję GetToken.

KROK 2 i KROK 3:

po stronie klienta:

function GetToken() { 
$.ajax({ 
    url: '@Url.Action("GetToken", "MyController")', 
    type: 'POST', 
    cache: 'false', 
    contentType: 'application/json; charset=utf-8', 
    dataType: 'text', 
    success: function (data) { 
     // data is already formatted in parameter string 
     SendCCDetailsToPaypal(data); 
    }, 
    //error: 
    //TODO Handle the BAD stuff 
});} 

Server Side:

Mam potrzebne oddzielne metody stosowane do budowy wszystkich wartości parametrów dla żądania tokena. Pierwsze trzy kompilacje: uwierzytelnianie, szczegóły transakcji, przezroczyste przekierowanie. W plikach web.config przechowuję informacje o adresach URL i płatnościach. Ostatnia metoda, ProcessTokenTransaction, wykonuje wszystkie ciężkie operacje, aby skontaktować się z Paypal przez WebRequest, a następnie przeanalizować go pod adresem URL, który zostanie odesłany do klienta. Ta metoda powinna zostać refaktoryzowana w celu uzyskania czystszej dostawy, ale pozostawiam to tobie. ParseResponse to metoda zapełniająca prosty model, który utworzyłem i zwraca ten model.

URL tokenu (sandbox):https://pilot-payflowpro.paypal.com

Różni się to od tokena URL !! Używany w wartości konfiguracyjnej PaypalTranactionAPI.

URL dla transakcji: (sandbox)https://pilot-payflowlink.paypal.com

private string PrepareApiAuthenticationParams()   
    { 
     var paypalUser = ConfigurationManager.AppSettings["PaypalUser"]; 
     var paypalVendor = ConfigurationManager.AppSettings["PaypalVendor"]; 
     var paypalPartner = ConfigurationManager.AppSettings["PaypalPartner"]; 
     var paypalPw = ConfigurationManager.AppSettings["PaypalPwd"]; 

     //var amount = (decimal)19.53; 

     var apiParams = @"USER=" + paypalUser 
         + "&VENDOR=" + paypalVendor 
         + "&PARTNER=" + paypalPartner 
         + "&PWD=" + paypalPw 
         + "&TENDER=C" 
         + "&TRXTYPE=A" 
         + "&VERBOSITY=HIGH"; 

     // find more appropriate place for this param 
     //+ "&VERBOSITY=HIGH"; 

     return apiParams; 
    } 


    private string PrepareTransactionParams(CustomerDetail detail) 
    { 
     var currencyType = "USD"; 

     var transactionParams = @"&BILLTOFIRSTNAME=" + detail.FirstName 
           + "&BILLTOLASTNAME=" + detail.LastName 
           + "&BILLTOSTREET=" + detail.Address1 
           + "&BILLTOSTREET2=" + detail.Address2 
           + "&BILLTOCITY=" + detail.City 
           + "&BILLTOSTATE=" + detail.State 
      //+ "&BILLTOCOUNTRY=" + detail.Country + // NEEDS 3 digit country code 
           + "&BILLTOZIP=" + detail.Zip 
           + "&BILLTOPHONENUM=" + detail.PhoneNum 
           + "&EMAIL=" + detail.Email 
           + "&CURRENCY=" + currencyType 
           + "&AMT=" + GET_VALUE_FROM_DB 
           + "&ERRORURL= " + HostUrl + "/Checkout/Error" 
           + "&CANCELURL=" + HostUrl + "/Checkout/Cancel" 
           + "&RETURNURL=" + HostUrl + "/Checkout/Success"; 

     // ADD SHIPTO info for address validation 

     return transactionParams; 
    } 


private string PrepareTransparentParams(string requestId, string transType) 
    { 
     var transparentParams = @"&TRXTYPE=" + transType + 
           "&SILENTTRAN=TRUE" + 
           "&CREATESECURETOKEN=Y" + 
           "&SECURETOKENID=" + requestId; 

     return transparentParams; 
    } 


    // Method to build parameter string, and create webrequest object 
public string ProcessTokenTransaction() 
    { 
     var result = "RESULT=0"; // default failure response 
     var transactionType = "A"; 
     var secureToken = string.Empty; 
     var requestId = Guid.NewGuid().ToString().Replace("-", string.Empty); 

     var baseUrl = ConfigurationManager.AppSettings["PaypalGatewayAPI"];    

     var apiAuthenticationParams = PrepareApiAuthenticationParams(); 

     // Create url parameter name/value parameter string 
     var apiTransactionParams = PrepareTransactionParams(detail); 

     // PCI compliance, Create url parameter name/value parameter string specific to TRANSAPARENT PROCESSING 
     var transparentParams = PrepareTransparentParams(requestId, transactionType); 

     var url = baseUrl; 
     var parameters = apiAuthenticationParams + apiTransactionParams + transparentParams; 


     // base api url + required 
     var request = (HttpWebRequest)WebRequest.Create(url); 
     request.Method = "POST"; 
     request.ContentType = "text/name"; // Payflow? 
     request.Headers.Add("X-VPS-REQUEST-ID", requestId); 

     byte[] bytes = Encoding.UTF8.GetBytes(parameters); 
     request.ContentLength = bytes.Length; 

     Stream requestStream = request.GetRequestStream(); 
     requestStream.Write(bytes, 0, bytes.Length); 
     requestStream.Close(); 


     WebResponse response = request.GetResponse(); 
     Stream stream = response.GetResponseStream(); 
     StreamReader reader = new StreamReader(stream); 

     try 
     { 

      // sample successful response 
      // RESULT=0&RESPMSG=Approved&SECURETOKEN=9pOyyUMAwRUWmmv9nMn7zhQ0h&SECURETOKENID=5e3c50a4c3d54ef8b412e358d24c8915 

      result = reader.ReadToEnd(); 

      var token = ParseResponse(result, requestId, transactionType); 

      var transactionUrl = ConfigurationManager.AppSettings["PaypalTransactionAPI"]; 
      secureToken = transactionUrl + "?SECURETOKEN=" + token.SecureToken + "&SECURETOKENID=" + requestId; 

      //ameValueCollection parsedParams = HttpUtility.ParseQueryString(result);     

      stream.Dispose(); 
      reader.Dispose(); 
     } 
     catch (WebException ex) 
     { 
      System.Diagnostics.Trace.WriteLine(ex.Message); 

     } 
     finally { request.Abort(); } 

     return secureToken; 
    } 


private TokenResponse ParseResponse(string response, string requestId, string transactionType) 
    { 
     var nameValues = HttpUtility.ParseQueryString(response); 

     int result = -999; // invalid result to guarantee failure 

     int.TryParse(nameValues.Get(TokenResponse.ResponseParameters.RESULT.ToString()), out result); 

     // retrieving response message 
     var responseMessage = nameValues.Get(TokenResponse.ResponseParameters.RESPMSG.ToString()); 

     // retrieving token value, if any 
     var secureToken = nameValues.Get(TokenResponse.ResponseParameters.SECURETOKEN.ToString()); 

     var reference = nameValues.Get(TokenResponse.ResponseParameters.PNREF.ToString()); 

     var authCode = nameValues.Get(TokenResponse.ResponseParameters.AUTHCODE.ToString()); 

     var cscMatch = nameValues.Get(TokenResponse.ResponseParameters.CSCMATCH.ToString()); 

     // populating model with values 
     var tokenResponse = new TokenResponse 
     { 
      Result = result, 
      ResponseMessage = responseMessage, 
      SecureToken = secureToken, 
      TransactionIdentifierToken = requestId, 
      TransactionType = transactionType, 
      ReferenceCode = reference, 
      AuthorizationCode = authCode, 
      CSCMatch = cscMatch 
     }; 

     return tokenResponse; 
    } 

etapach 4 i 5:

Powrót do Side Client:

Tutaj używam URL wbudowanego z poprzednich kroków i dodaj ostatnie potrzebne parametry (informacje o bezpiecznej karcie kredytowej) za pomocą jQuery, a następnie REDIRECT t o Paypal.

function SendCCDetailsToPaypal(secureParm) { 

    //alert('in SendCCDetailsToPaypal:' + secureParm); 

    var secureInfo = '&ACCT=' + $('#ccNumber').val() + '&EXPDATE=' + $("#expMonth").val() + $("#expYear").val() + "&CSC=" + $('#ccSecurityCode').val(); 
    secureInfo = secureParm + secureInfo; 

    window.location.replace(secureInfo);    
} 

KROK 6:

Paypal będzie zakładać z powrotem do jednej z następujących metod: Cancel, błędu lub Return (nazwa metod cokolwiek chcesz w żądaniu tokena). Przetwórz odpowiedź i spójrz na zmienne zwrócone z Paypal, w szczególności RESULT i RESPMSG. Zapoznaj się z dokumentacją, aby uzyskać szczegółowe informacje, ponieważ możesz włączyć sprawdzanie poprawności adresów i szereg innych funkcji. Na podstawie odpowiedzi wyświetl, co jest odpowiednie. strona

server:

public ActionResult Cancel() 
    { 
     var result = ParseRequest(HttpUtility.UrlDecode(Request.Params.ToString())); 

     //return View("Return", result); 
    } 


    public ActionResult Error() 
    { 

     var result = ParseRequest(HttpUtility.UrlDecode(Request.Params.ToString())); 

     return View("Return", result); 
    } 


    public ActionResult Return() 
    { 
     var result = ParseRequest(HttpUtility.UrlDecode(Request.Params.ToString())); 

     return View("Return", result); 
    } 

Nadzieja to pomaga, i powodzenia! Odpowiem na pytania wyjaśniające, tak jak jestem w stanie. Dziękuję za sprawdzenie tego i pamiętaj, aby zapłacić dalej.

+1

Czy dokonałeś jakiejkolwiek weryfikacji w URL-u do powrotu przed zatrzymaniem płatności? na przykład sprawdzanie, czy jest to faktyczna transakcja i ktoś nie utworzył po prostu adresu URL powrotu. – Justin

+1

Dzięki za rozwiązanie. To działa dla mnie, ale musiałem ustawić "włącz bezpieczny token" na true na koncie menedżera, aby to działało. Czy ty też musisz to zrobić? – mridula