I want create a payments with 3ds. So i read the docs for one payment and created a code like this:
// init code
final PaymentAuthConfig.Stripe3ds2UiCustomization uiCustomization =
new PaymentAuthConfig.Stripe3ds2UiCustomization.Builder()
.setLabelCustomization(
new PaymentAuthConfig.Stripe3ds2LabelCustomization.Builder()
.setTextFontSize(12)
.build())
.build();
PaymentAuthConfig.init(new PaymentAuthConfig.Builder()
.set3ds2Config(new PaymentAuthConfig.Stripe3ds2Config.Builder()
.setTimeout(5)
.setUiCustomization(uiCustomization)
.build())
.build());
code for a payment
// payment method
PaymentMethodCreateParams params = cardInputWidget.getPaymentMethodCreateParams();
if (params != null) {
Map<String, String> extraParams = new HashMap<>();
extraParams.put("setup_future_usage", "off_session");
ConfirmPaymentIntentParams confirmParams = ConfirmPaymentIntentParams
.createWithPaymentMethodCreateParams(params, paymentIntentClientSecret, null, false, extraParams);
final Context context = getApplicationContext();
stripe = new Stripe(
context,
PaymentConfiguration.getInstance(context).getPublishableKey()
);
stripe.confirmPayment(PaymentUPD.this, confirmParams);
}
code for one payment is work, and its work with 3ds. But now i want to save all cards and then choose some of card from saved list of card.
So i save a card like this:
SourceParams cardSourceParams = SourceParams.createCardParams(cardToSave);
Map<String, Object> params = new HashMap<String, Object>();
params.put("statement_descriptor", nameOfCard);
cardSourceParams.setExtraParams(params);
stripe.createSource(cardSourceParams, new ApiResultCallback<Source>() {
#Override
public void onSuccess(#NonNull Source source) {
Log.e("success", source.getId());
String source_id = source.getId();
// then i save this source to server
}
#Override
public void onError(#NonNull Exception error) {
Log.e("PaymentCore", error.getMessage(), error);
}
});
After my saving card, i get all list of card from server and i have something like this:
brand == Visa
last 4 numbers == 4242
source_id == src_1GSficBnnQZzyRulVXsNTThC1
so now i want to pay with a saved card. So i get
source_id == src_1GSficBnnQZzyRulVXsNTThC1
and try do something like this:
String source = "src_1GSficBnnQZzyRulVXsNTThC1";
SourceParams sourceParams = SourceParams.createSourceFromTokenParams(source);
ConfirmPaymentIntentParams confirmParams = ConfirmPaymentIntentParams
.createWithSourceParams(sourceParams, paymentIntentClientSecret, null);
final Context context = getApplicationContext();
stripe = new Stripe(
context,
PaymentConfiguration.getInstance(context).getPublishableKey()
);
stripe.confirmPayment(PaymentUPD.this, confirmParams);
error:
java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter returnUrl
what should i put to returnUrl ? I haw no any returnUrl
My second question is: How i can make a payment with 3ds with a already saved cards?
You should avoid using "sources" for any new Card-related integrations, and should prefer to use Payment Methods instead, just as with your one-time payment.
Stripe has a complete guide for saving card details for later use without payment. Use this if you do not intend to take payment up front. It includes a section for how to take payment with the card later.
If you do intend to take payment right away and save the card for later, follow the guide for that scenario instead.
Note that in all cases if a card has been previously authenticated via 3ds, you must always be ready to handle exceptions or authentication challenges with a recovery flow for your customer.
Related
I am writing an Android App to access Google Drive Photos via CloudRail service. I am able to authenticate to the Google account in question and see all my files/folders in the Google Drive, but I can't access photos from Google Photos.
While browsing through the Drive API documentation, it makes a reference to spaces, specifically 3 spaces are defined: drive, photos and allDataFolder.
Where do I specify the spaces that I am interested in? But default, the drive space is being accessed. Even though, I specifically specified scope for photos:
https://www.googleapis.com/auth/drive.photos.readonly
And when Google Authentication pages opens in the mobile browser, it states that my app wants to gain access to the user's Google Photos and I grant this access. But when calling CloudRail service to get children, no photos are visible
`googledriveChildren = mGoogledriveService.getChildren("/"); // returns goole drive top level files/folders
`googledriveChildren = mGoogledriveService.getChildren("/photos"); // generates a NotFoundException
I have already been down this path and achieved the integration - with the help/guidance from the folks at Cloudrail. You should note that my integration is limited to reading/downloading from Google Photos. I have not found any way to write/upload. Nor have I found any way of reading the album structure that can be set up in Google Photos.
First, you need to include the scope for Google Photos. I did this as follows:
public static final String GOOGLE_PHOTOS_SCOPE = "https://www.googleapis.com/auth/drive.photos.readonly";
private final AtomicReference<CloudStorage> googlephotos = new AtomicReference<>();
List<String> scope = new ArrayList<>();
scope.add(My_Constants.GOOGLE_PHOTOS_SCOPE);
googlephotos.set(new GoogleDrive(context, google_client_id, "", Get.GetString(R.string.google_redirect_uri),
Get.GetString(R.string.google_authentication_state), scope));
((GoogleDrive) googlephotos.get()).useAdvancedAuthentication();
You then need to build a Cloudrail advancedRequest to download whatever data you want. I download the metadata I require as follows:
CloudStrorage service = googlephotos.get();
private void searchForGooglePhotos(final CloudStorage service) throws Throwable {
GoogleDrive google_drive = (GoogleDrive) service;
boolean more = true;
String pageToken = null;
while (more) {
StringBuilder builder = new StringBuilder();
String query = URLEncoder.encode("mimeType='image/jpeg' and trashed = false", "utf-8");
builder.append("/files?spaces=photos");
if (pageToken != null) {
builder.append("&pageToken=");
builder.append(pageToken);
}
builder.append("&q=");
builder.append(query);
builder.append("&fields=nextPageToken,files(id,name,modifiedTime,description,size," +
"imageMediaMetadata(height,rotation,width,time))");
AdvancedRequestSpecification specification = new AdvancedRequestSpecification(builder.toString());
AdvancedRequestResponse response = google_drive.advancedRequest(specification);
#SuppressWarnings("unchecked")
Map<String, Object> resultObjectMap = (Map<String, Object>) response.getBodyJsonParsed();
pageToken = (String) resultObjectMap.get("nextPageToken");
#SuppressWarnings("unchecked")
ArrayList<Map<String, Object>> filesObjectMap = ((ArrayList<Map<String, Object>>) resultObjectMap.get("files"));
for (Map<String, Object> fileObjectMap : filesObjectMap) {
// process downloaded files
}
more = (pageToken != null);
}
}
Subsequently in my app I use Glide to download the photos themselves when required. In the Glide DataFetcher I obtain the inputStream using:
if (model.getSourceRecord().isTypeGooglePhotos()) {
AdvancedRequestSpecification specification;
AdvancedRequestResponse response;
if (model.getIsThumbnail()) {
specification = new AdvancedRequestSpecification("/files" + model.getSourceId() +
"?spaces=photos&fields=thumbnailLink");
response = ((GoogleDrive) service).advancedRequest(specification);
#SuppressWarnings("unchecked")
Map<String, Object> parsed = (Map<String, Object>) response.getBodyJsonParsed();
String link = (String) parsed.get("thumbnailLink");
specification = new AdvancedRequestSpecification(link);
specification.disableBaseUrl();
} else {
specification = new AdvancedRequestSpecification("/files" + model.getSourceId() + "?spaces=photos&alt=media");
}
response = ((GoogleDrive) service).advancedRequest(specification);
input_stream = response.getBodyAsStream();
} else {
if (model.getIsThumbnail()) {
input_stream = service.getThumbnail(model.getSourceId());
} else {
input_stream = service.download(model.getSourceId());
}
}
Here, "model" contains various info associated with each photo. The sourceId comes from the "id" downloaded:
String source_id = java.io.File.separator + fileObjectMap.get("id");
I hope this helps.
Would anyone arriving at this question / response please note that, as of mid Jan 2018, Google have "sunset" (sic) the photos space (spaces=photos above). This means that the above solution no longer works.
On the Google REST API documentation: "The photos space will sunset in early January 2018. Your users can continue to access Google Photos via the drive space by enabling the Google Photos folder in My Drive in the Drive client settings"
Ugh!
I'm almost finished with my application where I'm able to do a simple delete off of Google Spraedsheet data. However, I have not been able to find a method where I could add the sheetId and its respective GID # to the request arraylist.
private void deleteRow()
{
List<Request> requests = new ArrayList<>();
DeleteDimensionRequest deleteDimensionRequest = new DeleteDimensionRequest();
DimensionRange dimensionRange = new DimensionRange();
dimensionRange.getDimension();
dimensionRange.setStartIndex(13);
dimensionRange.setEndIndex(14);
deleteDimensionRequest.setRange(dimensionRange);
Sheets.Spreadsheets spreadsheets = null;
requests.add(new Request()
//There should be a function call or some sort for me to
//add a sheetid... if I do the updatesheets property here
//I get an error message saying that there's already a kind
//and I cannot set the id
.setDeleteDimension(deleteDimensionRequest)
);
BatchUpdateSpreadsheetRequest batchUpdateRequest = new BatchUpdateSpreadsheetRequest()
.setRequests(requests);
try
{
mService.spreadsheets().batchUpdate("SPREADSHEETID GOES HERE", batchUpdateRequest).execute();
}
catch(IOException e)
{
e.printStackTrace();
}
}
Does anyone know the strategy to add the sheet values into the request arraylist?
The function calls were actually available after creating a new constructor for DimensionRange.
Simply do:
dimensionRange.setDimension("ROWS");
dimensionRange.setSheetId(XXXXX);
to finish the JSON post request to Sheets API...
I am inserting data into a spreadsheet with the new Google Sheets API v4, the code works perfect and the data it is inserted well in the sheet.
But how to find out the last row with data to add the data after this ?
List<List<Object>> arrData = getData();
ValueRange oRange = new ValueRange();
oRange.setRange("Pedidos!AXXXXXXX"); // I NEED THE NUMBER OF THE LAST ROW
oRange.setValues(arrData);
List<ValueRange> oList = new ArrayList<>();
oList.add(oRange);
BatchUpdateValuesRequest oRequest = new BatchUpdateValuesRequest();
oRequest.setValueInputOption("RAW");
oRequest.setData(oList);
BatchUpdateValuesResponse oResp1 = mService.spreadsheets().values().batchUpdate("ID_SPREADSHEET", oRequest).execute();
Is there some trick in the A1 notation for this?
I need an equivalent to .getLastRow from Google Apps Script.
If you use the append feature and set the range to the entire sheet, the API will find the last row and append the new data after it.
This web page explains it.
https://developers.google.com/sheets/api/guides/values#appending_values
Here is some sample code:
String range="Sheet1";
insertData.setValues(rows);
AppendValuesResponse response=service.spreadsheets().values()
.append(spreadsheetId,range,insertData).setValueInputOption("USER_ENTERED")
.execute();
Note that the response will tell you where it was inserted.
The v4 API has no way to ask "what is the last row with data", as it's a different style of API than the Apps Script API. You can infer the last row yourself by requesting the data and counting the offset from your first requested row to the last returned row.
You can use AppendCellsRequest to append a row. The below methods should get you going. I haven't included the getRowDataListForCellStrings method as it is rather application specific.
First create a Request object containing a AppendCellsRequest:
public BatchUpdateSpreadsheetResponse appendWorksheet(String cellValues) throws SpreadsheetException {
AppendCellsRequest appendRequest = new AppendCellsRequest();
appendRequest.setSheetId( mSheet.getProperties().getSheetId() );
appendRequest.setRows( getRowDataListForCellStrings(cellValues) );
appendRequest.setFields("userEnteredValue");
Request req = new Request();
req.setAppendCells( appendRequest );
return executeBatchRequest(req);
}
Then call batchUpdate on the spreadsheets() interface:
BatchUpdateSpreadsheetResponse executeBatchRequest(Request request) throws SpreadsheetException {
List<Request> requests = new ArrayList<>();
requests.add( request );
BatchUpdateSpreadsheetRequest batchRequest = new BatchUpdateSpreadsheetRequest();
batchRequest.setRequests( requests );
try {
return mService.spreadsheets().batchUpdate(mSpreadsheet.getSpreadsheetId(), batchRequest).execute();
} catch (IOException e) {
throw new SpreadsheetException(e);
}
}
Unfortunately there doesn't seem to be a way to know which rows were updated. Not does is seem possible to set valueInputOption when appending in this way.
There is actually a way to ask the API.
I am using this on my node.js client (pretty sure it's very similar)
var sheets = google.sheets('v4');
sheets.spreadsheets.get({
auth: auth,
spreadsheetId: 'spreadsheet_id',
includeGridData: true
}, function (err, response) {
if (err) {
console.log('The API returned an error: ' + err);
} else {
var last_row = response.sheets[0].data[0].rowData.length;
}
});
I am using the Salesforce SDK (4.1.x) in a native Android app. I use the RestClient.sendAsync method to post my form data to a custom object. That part is working fine. Now I need to upload and attach a photo that was taken by the mobile user. I see that RestClient has an uploadFile method. Is this the correct method? If so then how do I connect the uploaded file to the custom form data?
Ok. I figured this out. First, create the parent object (the main form data) using the following.
request = RestRequest.getRequestForCreate(apiVersion, objectType, fields);
client.sendAsync(restRequest, new RestClient.AsyncRequestCallback() {...
In the onSuccess method you will get the id of the new object from the response. There are plenty of examples that show how to get the JSON object and the id. Armed with this parentId we can now create the attachment. The code looks something like this.
private void postImageAsAttachment(String parentId, String title) {
Map<String, Object> fields = new HashMap<String, Object>();
fields.put("Name", title);
fields.put("ParentId", parentId);
fields.put("Body", ImageHelper.getBase64FromImage(mCurrentPhotoPath));
RestRequest request = null;
try {
request = RestRequest.getRequestForCreate(apiVersion, "Attachment", fields);
} catch (Exception ex) {
Log.d(TAG, "sendRequest: ", ex);
Toast.makeText(MainActivity.this, "The file upload failed: " + ex.toString(), Toast.LENGTH_LONG).show();
}
client.sendAsync(request, new RestClient.AsyncRequestCallback() {...
I'm using a simple class called ImageHelper that simply loads the image file, performs image compression (if necessary), and base64 encodes the image data. The result is that an "Attachment" object is created as a child of the parent object.
I hope this helps the next person.
I an using the Stripe Android API to process payments in a mobile application. When a user registers for the app, a Stripe token is generated based on their credit card information. As per the requirement of the project, the app needs to reject prepaid credit cards from being used. Unfortunately, the Android API does not give access to the funding parameter of the Card object returned from the stripe token. This parameter states weather the card is prepaid, debit or credit.
Does anyone know how to access this information on Android? If this is not possible using the Stripe Android API, is there anyway I can directly access the JSON object returned from the Stripe API?
The Stripe Android bindings do not directly expose the card object from the API (which has the funding attribute you're interested in), but you can retrieve it by the token ID using the Java bindings. Something like this should work:
public void onSuccess(Token token) {
com.stripe.model.Token stripeToken = com.stripe.model.Token.retrieve(token.getId(), publishableKey);
com.stripe.model.Card stripeCard = stripeToken.getCard();
if (stripeCard.getFunding().equals("prepaid") {
// Reject card
}
}
Note that this is basically what the Android bindings do when creating the token in the first place (see here).
EDIT: After investigating a bit further, I'm not so sure the above will work. While there is a requestToken() method in the Android bindings that calls the retrieve token API with the publishable key (see here), I don't think it actually works. Calling this endpoint with a publishable key results in an error indicating that the secret key should be used. (I guess it was an undocumented behavior that was removed at some point.)
If this is the case, then I guess you have two options:
patch the Android bindings to make the funding property from the API object available in the Android object (at creation time), or
make the check server-side.
What about this? I used Ywain answer and put it into Async like Stripe SDK do. Works for me.
private class CreateStripeTokenTask extends AsyncTask<Void, Void, com.stripe.model.Token> {
private Map<String, Object> mMap;
protected CreateStripeTokenTask(Card card) {
mMap = hashMapFromCard(card);
}
#Override
protected com.stripe.model.Token doInBackground(Void... params) {
try
{
RequestOptions requestOptions = RequestOptions.builder().setApiKey(<YOUR_STRIPE_PUBLISHABLE_KEY>).build();
com.stripe.model.Token stripeToken = com.stripe.model.Token.create(mMap, requestOptions);
return stripeToken;
}
catch (Exception e)
{
return null;
}
}
protected void onPostExecute(com.stripe.model.Token stripeToken) {
if (stripeToken != null && stripeToken.getCard() != null)
{
if ("prepaid".equals(stripeToken.getCard().getFunding()))
{
//error - prepaid cards are not eligible
}
else
{
//card is fine
}
}
}
private Map<String, Object> hashMapFromCard(Card card) {
Map<String, Object> tokenParams = new HashMap<>();
Map<String, Object> cardParams = new HashMap<>();
cardParams.put("number", TextUtils.nullIfBlank(card.getNumber()));
cardParams.put("cvc", TextUtils.nullIfBlank(card.getCVC()));
cardParams.put("exp_month", card.getExpMonth());
cardParams.put("exp_year", card.getExpYear());
cardParams.put("name", TextUtils.nullIfBlank(card.getName()));
cardParams.put("currency", TextUtils.nullIfBlank(card.getCurrency()));
cardParams.put("address_line1", TextUtils.nullIfBlank(card.getAddressLine1()));
cardParams.put("address_line2", TextUtils.nullIfBlank(card.getAddressLine2()));
cardParams.put("address_city", TextUtils.nullIfBlank(card.getAddressCity()));
cardParams.put("address_zip", TextUtils.nullIfBlank(card.getAddressZip()));
cardParams.put("address_state", TextUtils.nullIfBlank(card.getAddressState()));
cardParams.put("address_country", TextUtils.nullIfBlank(card.getAddressCountry()));
// Remove all null values; they cause validation errors
for (String key : new HashSet<>(cardParams.keySet())) {
if (cardParams.get(key) == null) {
cardParams.remove(key);
}
}
tokenParams.put("card", cardParams);
return tokenParams;
}
}