how to prevent to customer to add same credit card - stripe-payments

I am using stripe as my payment provider and storing encrypted credit card id in my db returned from stripe.
My question is that from GUI customer can add same card again. I see stripe do not prevent to add same card multiple time for same customer. Since stripe always generates different encrypted card id for same card so I can't use it to validate if same card is being added again.
How can stop customer to add same card again again.

Looks like I got that . I can use fingerprint returned in json response. I saw stripe dashboard and found that fingerprint is always same for same card which I was adding again again.
Here is json request and response to confirm
Request
{
"source": {
"number": "378282246310005",
"cvc": "123",
"address_line2": "4th Floor",
"address_line1": "140 2nd Street",
"address_country": "USA",
"name": "VIAY KUMAR",
"address_state": "CA",
"exp_month": 12,
"exp_year": 2015,
"address_zip": "94105",
"address_city": "San Francisco",
"object": "card"
}
}
Response
{
"id": "card_166H9rC8Y8JrMFgBh9GVsmNG",
"object": "card",
"status": null,
"exp_month": 12,
"exp_year": 2015,
"last4": "0005",
"country": "US",
"type": null,
"name": "VIAY KUMAR",
"customer": "cus_6IrxhfwXNyD1Uw",
"recipient": null,
"address_line1": "140 2nd Street",
"address_line2": "4th Floor",
"address_zip": "94105",
"address_city": "San Francisco",
"address_state": "CA",
"address_country": "USA",
"address_zip_check": "pass",
"address_line1_check": "pass",
"cvc_check": "pass",
"fingerprint": "TwjSA2KqPDhSMUvQ",
"brand": "American Express",
"funding": "credit"
}
added same card again and got different card id but same finger print :-)
Request
{
"source": {
"number": "378282246310005",
"cvc": "123",
"address_line2": "4th Floor",
"address_line1": "140 2nd Street",
"address_country": "USA",
"name": "VIAY KUMAR",
"address_state": "CA",
"exp_month": 12,
"exp_year": 2015,
"address_zip": "94105",
"address_city": "San Francisco",
"object": "card"
}
}
Response
{
"id": "card_166HKVC8Y8JrMFgBfvbHPgk2",
"object": "card",
"status": null,
"exp_month": 12,
"exp_year": 2015,
"last4": "0005",
"country": "US",
"type": null,
"name": "VIAY KUMAR",
"customer": "cus_6IrxhfwXNyD1Uw",
"recipient": null,
"address_line1": "140 2nd Street",
"address_line2": "4th Floor",
"address_zip": "94105",
"address_city": "San Francisco",
"address_state": "CA",
"address_country": "USA",
"address_zip_check": "pass",
"address_line1_check": "pass",
"cvc_check": "pass",
"fingerprint": "TwjSA2KqPDhSMUvQ",
"brand": "American Express",
"funding": "credit"
}

Thanks to Vijay for the above answer.
I've written the following Ruby code in my Rails app to check for this.
Replace the relevant #user.stripe_customer_id variable with your own.
# Retrieve the customer we're adding this token to
customer = Stripe::Customer.retrieve(#user.stripe_customer_id)
# Retrieve the token
token = Stripe::Token.retrieve(params[:stripeToken])
# The fingerprint of the card is stored in `card.fingerprint`
card_fingerprint = token.card.fingerprint
# Check if the card fingerprint submitted matches one of the customer's current sources
fingerprint_already_exists = customer.sources.any? {|source| source[:fingerprint] == card_fingerprint}
if fingerprint_already_exists
# You can do whatever you want here. I've personally set a flash message to let the user know this card is already on their account
flash[:warning] = "That card already exists on your account."
redirect_to user_path(#user) and return
end
# Continue adding the source as normal
customer.sources.create(source: params[:stripeToken])
# Anything else you want to do...
flash[:success] = "Your account has been updated."
redirect_to user_path(#user) and return

The fingerprint generated by Stripe is unique to each card. As taken from the docs:
fingerprint string
Uniquely identifies this particular card number. You can use this attribute to check whether two customers who’ve signed up with you are using the same card number, for example.
Assuming you already have a customer object at this point (either a new one or one to update), this will let you add a card and make it default, without duplicates:
// $customer = \Stripe\Customer::retrieve( $clicker->customer );
/**
* Add the card, but only if it does not exist. Make it default in any case.
**/
$token = \Stripe\Token::retrieve( $_POST['stripeToken'] );
$card = null;
/** Does this customer already have this card? If so, find it. **/
foreach ( $customer->sources['data'] as $source ) {
if ( $source['fingerprint'] == $token['card']->fingerprint ) {
$card = $source;
}
}
/** If not, add the new card to the customer **/
if ( is_null( $card ) ) {
$card = $customer->sources->create( array( 'source' => $_POST['stripeToken'] ) );
}
$customer->default_source = $card->id;
$customer->save();
You'd enclose this in the usual try/catch as exemplified by the docs.
That being said, from the point of view of the user, it will be better, if possible, to show them a list of their cards (last4 + expire date) and let them pick the one they'd like to charge – instead of asking for their info needlessly.

Related

How to replace existing key in jsonb?

I'm trying to update a jsonb array in Postgres by replacing the entire array. It's important to note, I'm not trying to add an array to the object, but simply replace the whole thing with new values. When I try the code below, I get this error in the console
error: cannot replace existing key
I'm using Nodejs as server-side language.
server.js
//new array with new values
var address = {
"appt": appt,
"city": city,
"street": street,
"country": country,
"timezone": timezone,
"coordinates": coordinates,
"door_number": door_number,
"state_province": state_province,
"zip_postal_code": zip_postal_code
}
//query
var text = "UPDATE users SET info = JSONB_insert(info, '{address}', '" + JSON.stringify(address) + "') WHERE id=$1 RETURNING*";
var values = [userid];
//pool...[below]
users table
id(serial | info(jsonb)
And this is the object I need update
{
"dob": "1988-12-29",
"type": "seller",
"email": "eyetrinity3#test.com",
"phone": "5553766962",
"avatar": "f",
"address": [
{
"appt": "",
"city": "Brandon",
"street": "11th Street East",
"country": "Canada",
"timezone": "Eastern Standard Time",
"coordinates": [
"-99.925011",
"49.840649"
],
"door_number": "666",
"state_province": "Manitoba",
"zip_postal_code": "R7A 7B8"
}
],
"last_name": "doe",
"first_name": "john",
"date_created": "2022-11-12T19:44:36.714Z",
}
below works in db-fiddle Postgresql v15 (did not in work in v12)
specific element
update json_update_t set info['address'][0] = '{
"appt": "12",
"city": "crater",
"street": "11th Street East",
"country": "mars",
"timezone": "Eastern Standard Time",
"coordinates": [
"-99.925011",
"49.840649"
],
"door_number": "9999",
"state_province": "marsbar",
"zip_postal_code": "abc 123"
}';
whole array
update json_update_t set info['address'] = '[{
"appt": "14",
"city": "crater",
"street": "11th Street East",
"country": "mars",
"timezone": "Eastern Standard Time",
"coordinates": [
"-99.925011",
"49.840649"
],
"door_number": "9999",
"state_province": "marsbar",
"zip_postal_code": "abc 123"
}]';
I have found the answer for this. Going through some of my older apps I coded, I stumbled upon the answer. It's not JSONB_INSERT but JSONB_SET. Notice the difference. The later will replace the entire key and not insert or add to the object.
JSONB_INSERT --> insert
UPDATE users SET info = JSONB_insert(info, '{address,-1}', '" + JSON.stringify(address) + "',true) WHERE id=$1 RETURNING*
JSONB_SET --> set and replace
UPDATE users SET info = JSONB_SET(info, '{address}', '" + JSON.stringify(address) +"') WHERE id=$1 RETURNING*

Stripe source.chargeable event with null properties like amount and currency

Is it correct to charge a source based on the event data's amount and currency? Sometimes I receive events like:
{
"object": {
"id": "src_1Ch7qCIhY6Z3DMWts3HPpWoH",
"object": "source",
"amount": null,
"card": {
"exp_month": 2,
"exp_year": 2022,
"address_zip_check": "unchecked",
"brand": "Visa",
"card_automatically_updated": false,
"country": "US",
"cvc_check": "unchecked",
"fingerprint": "xGimquKE7Rnk7LK2",
"funding": "credit",
"last4": "3063",
"three_d_secure": "required",
"address_line1_check": null,
"tokenization_method": null,
"dynamic_last4": null
},
"client_secret": "src_client_secret_D7MZe67elxyyCGGAaUA1lCsS",
"created": 1529983660,
"currency": null,
"flow": "none",
"livemode": false,
"metadata": {
},
"owner": {
"address": {
"city": null,
"country": null,
"line1": null,
"line2": null,
"postal_code": "22222",
"state": null
},
"email": null,
"name": null,
"phone": null,
"verified_address": null,
"verified_email": null,
"verified_name": null,
"verified_phone": null
},
"statement_descriptor": null,
"status": "chargeable",
"type": "card",
"usage": "reusable"
},
"previous_attributes": null
}
Notice amount and currency are null. Why isit null even when I have set the amount and currency when creating the source? I also notice its sometimes set correctly. Should I be relying on this properties set? Or should I be storing them in my DB? And store this transaction ID from DB into the metadata of the source for example.
UPDATE
I realized that this event is called when my frontend creates a 3ds source.
stripe.createSource(card).then(function (result) {
console.log(result.source)
})
I am alittle confused, the docs did say
When creating a 3D Secure source, its status is most commonly first set to pending and cannot yet be used to create a charge. In some cases, a 3D Secure source’s status can be immediately set to chargeable. This can happen if the customer’s card has not yet been enrolled in 3D Secure.
But I am using the test card, 4000000000003063 which requires 3ds verification. Why is it immediately chargeable?
status: "chargeable"
type: "card"
usage: "reusable"
The source you are creating is a regular card source, which is distinct from a Three D Secure source. That card source itself is chargeable, but if you try to charge it you will see that it will be declined because the card requires 3DS.
If the card source indicates that Three D Secure is required, then you need to use it to create a 3DS source and proceed with charging that instead : https://stripe.com/docs/sources/three-d-secure#create-3ds-source

How do I specify VAT in an order?

We're based in the EU. When we sell our digital products to private persons or companies without a VAT number, we have to charge them VAT (Value Added Tax). This is what I'm trying:
import stripe
stripe.api_key = 'sk_test_xxx'
stripe.api_version = '2015-10-16'
product = stripe.Product.create(
id='product',
name='Product',
shippable=False
)
sku = stripe.SKU.create(
product='product',
price=100,
currency='eur',
inventory={'type': 'infinite'}
)
customer = stripe.Customer.create(
email='customer#example.org',
description="Customer"
)
order = stripe.Order.create(
customer=customer.id,
currency='eur',
items=[
{
'type': 'sku',
'quantity': 5,
'parent': sku.id,
'amount': 500
},
{
'type': 'tax',
'description': "20% VAT",
'amount': 100
}
]
)
The Order creation call gives me:
stripe.error.InvalidRequestError: Request req_xxx: Items of type tax are not supported at order creation.
When I replace the last order creation call without the tax:
order = stripe.Order.create(
customer=customer.id,
currency='eur',
items=[
{
'type': 'sku',
'quantity': 5,
'parent': sku.id,
'amount': 500
}
]
)
I'm getting back these order['items']:
[
{
"amount": 500,
"currency": "eur",
"description": "Product",
"object": "order_item",
"parent": "sku_xxx",
"quantity": 5,
"type": "sku"
},
{
"amount": 0,
"currency": "eur",
"description": "Taxes (included)",
"object": "order_item",
"parent": null,
"quantity": null,
"type": "tax"
},
{
"amount": 0,
"currency": "eur",
"description": "Free shipping",
"object": "order_item",
"parent": "ship_free-shipping",
"quantity": null,
"type": "shipping"
}
]
However, an order does not allow updating the items field after the order has been created.
What's the correct and semantic way to add VAT to the order items?
I contacted Stripe support and this should now be possible in a private beta. You can ask Stripe to join the taxes beta.
After joining, you can access documentation here: https://stripe.com/docs/relay#shipping-and-taxes and here: https://stripe.com/docs/relay/dynamic-shipping-taxes#order-creation-event.
There will be an option in your Stripe dashboard (Relay settings) to specify a "dynamic" taxes webhook where Stripe sends an Order to, and your server should then respond with a Order Item containing a tax entry. The webhook is hit immediately after creating an Order.

Update inner object in arangodb

I have an object stored in arangodb which has additional inner objects, my current use case requires that I update just one of the elements.
Store Object
{
"status": "Active",
"physicalCode": "99999",
"postalCode": "999999",
"tradingCurrency": "USD",
"taxRate": "14",
"priceVatInclusive": "No",
"type": "eCommerce",
"name": "John and Sons inc",
"description": "John and Sons inc",
"createdDate": "2015-05-25T11:04:14+0200",
"modifiedDate": "2015-05-25T11:04:14+0200",
"physicalAddress": "Corner moon and space 9 station",
"postalAddress": "PO Box 44757553",
"physicalCountry": "Mars Sector 9",
"postalCountry": "Mars Sector 9",
"createdBy": "john.doe",
"modifiedBy": "john.doe",
"users": [
{
"id": "577458630580",
"username": "john.doe"
}
],
"products": [
{
"sellingPrice": "95.00",
"inStock": "10",
"name": "School Shirt Green",
"code": "SKITO2939999995",
"warehouseId": "723468998682"
},
{
"sellingPrice": "95.00",
"inStock": "5",
"name": "School Shirt Red",
"code": "SKITO245454949495",
"warehouseId": "723468998682"
},
{
"sellingPrice": "95.00",
"inStock": "10",
"discount": "5%",
"name": "School Shirt Blue",
"code": "SKITO293949495",
"warehouseId": "723468998682"
}
]
}
I want to change just one of the products stock value
{
"sellingPrice": "95.00",
"inStock": "10",
"discount": "5%",
"name": "School Shirt Blue",
"code": "SKITO293949495",
"warehouseId": "723468998682"
}
Like update store product stock less 1 where store id = x, something to this effect
FOR store IN stores
FILTER store._key == "837108415472"
FOR product IN store.products
FILTER product.code == "SKITO293949495"
UPDATE product WITH { inStock: (product.inStock - 1) } IN store.products
Apart from the above possibly it makes sense to store product as a separate document in collection store_products. I believe in NOSQL that is the best approach to reduce document size.
Found answer
here arangodb-aql-update-single-object-in-embedded-array and there
arangodb-aql-update-for-internal-field-of-object
I however believe it is best to maintain separate documents and rather use joins when retrieving. Updates easily

Is it not possible to get the birthday of a user with JSON WS in Liferay?

I am using Liferay for user management and an external application that communicates with Liferay using JSON Web services. When I call the web service /user/get-user-by-id
{
"agreedToTermsOfUse": false,
"comments": "",
"companyId": 10153,
"contactId": 16003,
"createDate": 1390552990000,
"defaultUser": false,
"emailAddress": "basketball#liferay.com",
"emailAddressVerified": false,
"facebookId": 0,
"failedLoginAttempts": 0,
"firstName": "Basketball1",
"graceLoginCount": 0,
"greeting": "Welcome Basketball1!",
"jobTitle": "",
"languageId": "en_US",
"lastFailedLoginDate": null,
"lastLoginDate": null,
"lastLoginIP": "",
"lastName": "",
"ldapServerId": -1,
"lockout": false,
"lockoutDate": null,
"loginDate": null,
"loginIP": "",
"middleName": "",
"modifiedDate": 1390992862465,
"openId": "",
"portraitId": 18708,
"reminderQueryAnswer": "",
"reminderQueryQuestion": "",
"screenName": "basketball1",
"status": 0,
"timeZoneId": "Europe/Paris",
"userId": 16002,
"uuid": "3ce789e0-4cb5-45bf-b57e-68c44ea3ec04"
}
And some information as birthday is missed. I see that also I can change the birthday using the web services /user/update-user but I cannot retrieve the original value. How can I get the birthday information? Is there any other method to obtain the missing data?
And... if I want to update any field of the user and in /user/update-user I MUST put the birthday and I cannot retrieve the old value... the birthday will be lost!
You can get the user birthday through contact service:
http://localhost:8080/api/jsonws/contact/get-contact/contact-id/10202
where id is the user-id

Resources