📜 ⬆️ ⬇️

We build payment acceptance into a mobile application, or why you can forget about PCI DSS and PA DSS

Does PCI DSS need it?


Sooner or later, most owners and developers of online stores and mobile applications that accept payments online, ask: "Should my project comply with the requirements of PCI DSS standards?".

PCI DSS is a security standard that applies to all organizations processing payment cards: merchants, processing centers, financial institutions and service providers, as well as other organizations that store, process or transmit cardholder data and / or critical authentication data .

The PA-DSS standard applies to application providers and other application developers who store, process, or transmit cardholder data and / or critical authentication data.


image
')
The website is quite simple: when integrating, it is enough to use a technical solution that redirects the payer to the card data entry form located on the PCI DSS certified payment gateway site or downloads this page in the frame also from a certified site. In this case, the merchant does not comply with the security standard, since the card data is not stored or transmitted through its server, and the merchant site does not have access to the payment gateway frame due to the security policies of the web browsers.

With a mobile app, things are a little more complicated. There is a popular misconception that if a mobile application requests data of a card, then it automatically falls within the scope of the PCI DSS standard. But, in fact, the organization developing standards for PCI DSS (PCI SSC - Payment Card Industry Security Standards Council) has not yet released separate standards requirements for mobile applications. And this means that the standard is still not mandatory, but a recommendation character for the most popular category of mobile applications, namely :

Category 3. Payment applications that work on any household handheld devices (for example, smartphones, tablets, PDAs), whose functionality is limited not only by accepting payments.


But since a mobile application cannot exist without a backend (the server side serving billing and basic business logic), one way or another, it transmits the information necessary for payment processing to the merchant's server. Here lies the nuance - that, intentionally or accidentally, the developer of a mobile application does not program the application to transfer payment card data to any uncertified server, the mobile payment SDK must make the card data inaccessible for reading. This restriction ensures that the PCI DSS requirements are invalidated:

PCI DSS may not be distributed directly to payment application providers if they do not store, process or transfer cardholder data, or do not have access to cardholder data for their customers.


Consider how this is implemented in the mobile SDK of the Fondy payment service using the example of an Android solution (there is also an iOS SDK ).

The solution is to enter the card data into the View created by the SDK library, and the mobile application uses the public methods of this View to initialize the payment, style the form and get information about the completion of the payment.

An example of a demo application for Android


First of all, let's create a visual structure of our payment form - layout (by the way, all the source code of the demo application can be found in github ):

activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="10dp"> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/btn_amount" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/lbl_amount" /> <EditText android:id="@+id/edit_amount" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="7dp" android:maxLength="7" android:inputType="number" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="7dp" android:text="@string/lbl_ccy" /> <Spinner android:id="@+id/spinner_ccy" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="7dp" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="7dp" android:text="@string/lbl_email" /> <EditText android:id="@+id/edit_email" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="7dp" android:inputType="textEmailAddress" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="7dp" android:text="@string/lbl_description" /> <EditText android:id="@+id/edit_description" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="7dp" /> <com.cloudipsp.android.CardInputView android:id="@+id/card_input" android:layout_width="match_parent" android:layout_height="wrap_content"/> <TextView android:id="@+id/text_card_type" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:visibility="gone" /> <Button android:id="@+id/btn_pay" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:gravity="center" android:text="@string/btn_pay" /> </LinearLayout> </ScrollView> <com.cloudipsp.android.CloudipspWebView android:id="@+id/web_view" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone"/> </RelativeLayout> 



Please note that all elements, except for card data in the application, are their own, and the form for entering the card number, expiration date and CVV2 are encapsulated in the com.cloudipsp.android.CardInputView class. It looks like this (for tests, you can use the account information specified in the documentation: https://www.fondy.eu/ru/info/api/v1.0/2 ):

image

In this case, all elements, including those belonging to the com.cloudipsp.android.CardInputView class, can be easily styled as the design of the main application. Also, for the further work of the application with the cards connected to 3DSecure, we need an element of the com.cloudipsp.android.CloudipspWebView class - this is a WebView in which the payer will be redirected to the website of his issuing bank to enter a personal password (in this picture - the page emulates the work 3dsecure bank card issuer card:

image

We now turn to our main class, which will implement the application logic: public class MainActivity. Initialize the object of the Cloudipsp class:

  @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.btn_amount).setOnClickListener(this); editAmount = (EditText) findViewById(R.id.edit_amount); spinnerCcy = (Spinner) findViewById(R.id.spinner_ccy); editEmail = (EditText) findViewById(R.id.edit_email); editDescription = (EditText) findViewById(R.id.edit_description); cardInput = (CardInputView) findViewById(R.id.card_input); cardInput.setHelpedNeeded(BuildConfig.DEBUG); findViewById(R.id.btn_pay).setOnClickListener(this); webView = (CloudipspWebView) findViewById(R.id.web_view); cloudipsp = new Cloudipsp(MERCHANT_ID, webView); spinnerCcy.setAdapter(new ArrayAdapter<Currency>(this, android.R.layout.simple_spinner_item, Currency.values())); } 

Next, hang the object of the com.cloudipsp.android.Card class object handler to get the result of entering the card number:

  @Override public void onCardInputErrorClear(CardInputView view, EditText editText) { } @Override public void onCardInputErrorCatched(CardInputView view, EditText editText, String error) { editText.getText(); } 

Now we will know when the user completes data entry, or, in case of an error, we will receive information about the problem. After the card data is entered, we can create an order:

  if (card != null) { final Currency currency = (Currency) spinnerCcy.getSelectedItem(); final Order order = new Order(amount, currency, "vb_" + System.currentTimeMillis(), description, email); cloudipsp.pay(card, order, new Cloudipsp.PayCallback() { @Override public void onPaidProcessed(Receipt receipt) { Toast.makeText(MainActivity.this, "Paid " + receipt.status.name() + "\nPaymentId:" + receipt.paymentId+"\n Signature:"+receipt.signature, Toast.LENGTH_LONG).show(); } @Override public void onPaidFailure(Cloudipsp.Exception e) { if (e instanceof Cloudipsp.Exception.Failure) { Cloudipsp.Exception.Failure f = (Cloudipsp.Exception.Failure) e; Toast.makeText(MainActivity.this, "Failure\nErrorCode: " + f.errorCode + "\nMessage: " + f.getMessage() + "\nRequestId: " + f.requestId, Toast.LENGTH_LONG).show(); } else if (e instanceof Cloudipsp.Exception.NetworkSecurity) { Toast.makeText(MainActivity.this, "Network security error: " + e.getMessage(), Toast.LENGTH_LONG).show(); } else if (e instanceof Cloudipsp.Exception.ServerInternalError) { Toast.makeText(MainActivity.this, "Internal server error: " + e.getMessage(), Toast.LENGTH_LONG).show(); } else if (e instanceof Cloudipsp.Exception.NetworkAccess) { Toast.makeText(MainActivity.this, "Network error", Toast.LENGTH_LONG).show(); } else { Toast.makeText(MainActivity.this, "Payment Failed", Toast.LENGTH_LONG).show(); } e.printStackTrace(); } }); } 

As you can see, the integration is fairly simple and does not require much effort on the part of the application developer. At the same time, the SDK solves two problems at the same time - it gives the merchant a tool for accepting payments on payment cards and eliminates the need to be certified for safety standards.

Source: https://habr.com/ru/post/304650/


All Articles