I am building an app on iOS in swift and I am accepting payments using stripe. I have created a form that creates a customer and upon a successful save I call back the customerID and save it into my Parse Database.
Below is the code for the above:
XCODE:
let myCustomerID = user!.valueForKey("secretID") as! String
self.sendTokenToServer(myCustomerID, myAmount: cost)
func sendTokenToServer(aCustomer: String, myAmount: String) {
var theamount = myAmount
// SEND REQUEST TO PHP FILE IN WEBSERVER
let url = NSURL(string: ".../cutomeragain.php")
var request = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "POST"
let body = "custID=\(aCustomer)&amount=\(theamount)"
request.HTTPBody = body.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)
// SEND ASYNCHORNOUS REQUEST
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) {
(response, data, error) -> Void in
if error == nil {
}
}
When I go to charge the customer by ID, it does not accept the charge. Below is the PHP code:
<?php
require_once('stripe-php/init.php');
\Stripe\Stripe::setApiKey('xxx');
// Get the credit card details submitted by the form
$amount = $_POST['amount'];
$customerId = $_POST['custID'];
$cutomer = \Stripe\Customer::retrieve('custID');
// Charge the Customer instead of the card
$charge = \Stripe\Charge::create(array(
"amount" => $amount,
"currency" => "usd",
"customer" => $customerId)
);
// create a json output with completion variable, (this will be read from the ios app as response)
$json = array(
'completion' => 'done',
'completion1' => $cutomerId,
'ok' => $cutomer-id
);
echo json_encode($json);
?>
Am I missing something in my PHP file? I've tried a number of different methods. I.e. retrieving the customer from Stripe using
Ok so I figured it out. I had to play around a bit, however, I think that someone else will find this useful.
In my xcode file I changed
let myCustomerID = user!.valueForKey("secretID") as! String
to
let myCustomerID = user?["secretID"] as? String
In my PHP file I changed the code to the following:
// Get the credit card details submitted by the form
$amount = $_POST['amount'];
$customerId = $_POST['cus'];
$cu = \Stripe\Customer::retrieve($customerId);
// Charge the Customer instead of the card
$charge = \Stripe\Charge::create(array(
"amount" => $amount,
"currency" => "usd",
"customer" => $customerId,)
);
// create a json output with completion variable, (this will be read from the ios app as response)
$json = array(
'completion' => 'done',
'completion1' => $cutomerId,
'ok' => $cutomer-id
);
echo json_encode($json);
?>
Related
I have an iOS application (Swift) which encodes some data and does a JSONSerialization and creates a JSON object. My code is below, I've done my best to keep it tidy, so I hope it makes sense:
struct Order: Codable {
let idQty: [FoodIdAndQuantity]
let collection: String
let name: String
let phone: Int
let doorNum: Int
let street: String
let postcode: String
}
struct FoodIdAndQuantity: Codable {
let itemId: Int
let qty: Int
}
class CheckoutServer: NSObject, URLSessionDataDelegate {
var inputValuesForItemAndQuantity = [Int:Int]()
var idQty = [FoodIdAndQuantity]()
var collection = String()
var name = String()
var phone = Int()
var doorNum = Int()
var street = String()
var postcode = String()
var request = URLRequest(url: NSURL(string: "http://192.168.1.100/api/AddOrder.php")! as URL)
func sendToDatabase() {
for(key,value) in inputValuesForItemAndQuantity {
idQty.append(FoodIdAndQuantity(itemId: key, qty: value))
}
let order = Order(idQty: idQty,collection: collection,name: name,phone: phone,doorNum: doorNum,street: street,postcode: postcode)
let encodedOrder = try? JSONEncoder().encode(order)
var json: Any?
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
if let data = encodedOrder {
json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments)
if var json = json {
if JSONSerialization.isValidJSONObject(json) {
do {
json = try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)
} catch {
print("There was a problem creating the JSON object")
}
} else {
print("not valid JSON")
}
}
}
let postParameters = "json="+String(describing: json!)
print(String(describing: json!)) //Print JSON for debugging purposes
request.httpBody = postParameters.data(using: .utf8)
let defaultSession = URLSession(configuration: URLSessionConfiguration.default)
let task = defaultSession.dataTask(with: request) { (data, response, error) in
if error != nil {
print("Failed to download data at Menu Type Items")
} else {
print("Data uploaded")
}
}
task.resume()
}
}
So the above code does the following:
Creates an encodable object called 'order'.
Creates a POST request to my API.
Passes the encoded JSON object via a POST parameter.
I've printed the json object that gets posted back to the console in XCode and that looks as follows:
{
collection = Delivery;
doorNum = 99;
idQty = (
{
itemId = 17;
qty = 5;
},
{
itemId = 1;
qty = 3;
}
);
name = James;
phone = 012345667;
postcode = LXU49RT;
street = Hope Street;
}
Next, I'll move over to my server/API which accepts the POST parameter.
Below is my AddOrder.php page:
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
require_once dirname(__FILE__) . '/DbOperation.php';
$json = $_POST["json"];
$db = new DbOperation();
$json = $db->addOrder($json);
}
And below is my DbOperation addOrder function:
public function addOrder($json) {
require dirname(__FILE__) . '/../../dbconnect.php';
$decoded = json_decode($json);
$collection = $decoded{"collection"};
$stmt2 = $pdo->prepare("INSERT INTO TestTable (collection) VALUES (:collection)");
$stmt2->bindParam(':collection',$collection);
$stmt2->execute();
}
It's worth noting that, whilst I try to fix this issue, I have created a test table in my Database which simply stores the collection element of the JSON.
The problem I have is, when I run my application and send the data, nothing gets stored in the database, and my apache error.log file says the Column 'collection' cannot be null. So I assume I am handling the POST parameter incorrectly at some point of my PHP. Unless the fault lies at a Swift level, which I'll add the Swift tag to this post if asked by an admin.
The full error is below:
[Wed Feb 28 15:44:55.178184 2018] [:error] [pid 520] [client 192.168.1.46:52400] PHP Fatal error: Uncaught PDOException: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'collection' cannot be null in /var/www/api/DbOperation.php:111\nStack trace:\n#0 /var/www/api/DbOperation.php(111): PDOStatement->execute()\n#1 /var/www/api/AddOrder.php(16): DbOperation->addOrder(NULL)\n#2 {main}\n thrown in /var/www/api/DbOperation.php on line 111
What I've tried
I've tried altering my AddOrder.php page to the following:
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
require_once dirname(__FILE__) . '/DbOperation.php';
//$json = $_POST["json"];
$json = json_decode(file_get_contents('php://input'),true);
$db = new DbOperation();
$json = $db->addOrder($json);
}
Your swift code doesn't make much sense. You have code that uses a JSONEncoder to encode your swift object into Data. If that succeeds, you then convert the data back to a Swift object using JSONSerialization. if that succeeds, you then use JSONSerialization.data(withJSONObject:options:) to convert your Swift object back to JSON data, and then use String(describing:) your insanely over-processed JSON Data to a string, which is very, very wrong.
Get rid of all that code. Try this instead:
func sendToDatabase() {
for(key,value) in inputValuesForItemAndQuantity {
idQty.append(FoodIdAndQuantity(itemId: key, qty: value))
}
let order = Order(idQty: idQty,collection: collection,name: name,phone: phone,doorNum: doorNum,street: street,postcode: postcode)
guard let encodedOrder = try? JSONEncoder().encode(order) else { return }
request.httpBody = encodedOrder
let defaultSession = URLSession(configuration: URLSessionConfiguration.default)
let task = defaultSession.dataTask(with: request) { (data, response, error) in
if error != nil {
print("Failed to download data at Menu Type Items")
} else {
print("Data uploaded")
}
}
task.resume()
}
The following is not valid code, and should throw a fatal "Cannot use object of type stdClass as array":
$decoded = json_decode($json);
$collection = $decoded{"collection"};
You probably want this:
$decoded = json_decode($json, true);
$collection = $decoded["collection"];
Or this:
$decoded = json_decode($json);
$collection = $decoded->collection;
I noticed a json decoding issue with incoming json from Swift 5 to php. Using file_put_contents in my php API, I found the json string from Swift looks like this
[{"Description":"box 3 of 3","Name":"Box S-7","Barcode":"1007","ComponentID":"50","Notes":"ok"}]
which if you convert with json_decode will throw an error, unless you remove the [ and ] from beginning and end of the string, respectively
$new_json = substr($json, 1, -1);
$decode = json_decode($new_json);
I am currently linking my IOS app with the backend to process Stripe payments. I am using FileZilla to host the files, and cSpan to host the server. Here is my swift code:
let task = session.dataTask(with: request as URLRequest, completionHandler: {data, response, errorD -> Void in
if errorD == nil {
print("got response")
do {
let jsonResult: NSDictionary? = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
let jsonComp = jsonResult?.value(forKey: "completion") as! String
print(jsonComp)
} catch {
print(errorD?.localizedDescription)
// ERROR OCCURS HERE
}
//let jsonResult: Any! = JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
// print(jsonResult)
} else {
print("error")
}
}) task.resume()
And here is a screenshot of my FileZilla directory:
(I just realized that the picture doesn't actually show the file 'token.php' in the Payments directory, but it is there.)
As you can see, I am properly going into the correct directory to get to the file, and performing the right methods to get complete the HTTP request. What is strange, however, is that it will always cause an error after the 'catch', but it will display nil when I try to print the localized description of the error. Here is the php code inside of token.php
<?php
require_once('stripe-php/init.php');
\Stripe\Stripe::setApiKey("sk_test_eRFc0VoIi7mo7zBuArrznB5a");
// Get the credit card details submitted by the form
$token = $_POST['stripeToken'];
$amount = $_POST['amount'];
$type = $_POST['type'];
$name = $_POST['name'];
// Create the charge on Stripe's servers - this will charge the user's card
try {
$charge = \Stripe\Charge::create(array(
"amount" => $amount,
"currency" => "usd",
"source" => $token,)
);
//email the purcahse and code info to your email
mail("willcohen2000#gmail.com","Purchase done","amount: $amount , type: $type, name: $name");
// create a json output with completion variable, (this will be read from the ios app as response)
$json = array(
'completion' => 'done'
);
echo json_encode($json);
} catch(\Stripe\Error\Card $e) {
// The card has been declined
$errorMessage = $e->getMessage();
$json = array(
'completion' => $getMessage()
);
echo json_encode($json);
}
?>
As it may be a bit evident, I am relatively inexperienced at backend work, so any explanation or point in the correct direction will be highly appreciated. Thanks.
so I'm trying to create managed stripe accounts with PHP Swift and Alamofire. But it's not working at all.
Here's my PHP code:
<?php
require_once('vendor/autoload.php');
\Stripe\Stripe::setApiKey("My APIKEY");
$country = $_POST['country'];
$create = \Stripe\Account::create(array(
"country" => $country,
"managed" => true
)
);
?>
Here's my swift code:
#IBAction func createBtn(_ sender: Any) {
let card = STPCardParams()
card.number = "5200828282828210"
card.expMonth = 4
card.expYear = 2024
card.cvc = "242"
card.currency = "usd"
STPAPIClient.shared().createToken(withCard: card ,completion: {(token, error) -> Void in
if let error = error {
print("ERROR: \(error.localizedDescription)")
}
else if let token = token {
print(token)
self.createUsingToken(token:token)
}
})
}
func createUsingToken(token:STPToken) {
let requestString = "My request URL"
let params = ["token": token.tokenId, "country": "US"]
//This line of code will suffice, but we want a response
Alamofire.request(requestString, method: .post, parameters: params).responseJSON { (response) in
print("REQUEST: \(response.request!)") // original URL request
print("RESPONSE: \(response.response!)") // URL response
print("DATA: \(response.data!)") // server data
print("RESULT: \(response.result)") // result of response serialization
if let JSON = response.result.error {
print("JSON: \(JSON.localizedDescription)")
}
}
}
And I'm getting this error from Alamofire: JSON: Response could not be serialized, input data was nil or zero length.
Thanks for your help.
It looks like your Swift/Alamofire request is expecting a JSON response, but your PHP code is not sending any response at all: you're sending an account creation request to Stripe but then never outputting any data.
You likely want to prepare an array with the attributes that you expect in your Swift code, then output it as JSON at the end of your PHP script:
echo json_encode($result);
i am implementing a FCM app server and wanted to store the device registration token id into my own database (Server written in PHP).
I realise the data in device id token is always null, Can someone point the right way to store the token accordingly into database?
Really appreciated perhaps someone could guide me on this issue, thanks!
Kindly refer the code/screenshots below:-
AppDelegate.swift
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
var token = ""
for i in 0..<deviceToken.count {
token += String(format: "%02.2hhx", arguments: [deviceToken[i]])
}
print("Registration succeeded!")
print("Token: ", token)
Callquery(token)
}
AppDelegate.swift (Callquery method which send a POST request to server side script)
func Callquery(_ token: String)
{
// append parameter to oneDictionary
let tokenString = ["token": token] as [String: Any]
// create the request
var request = URLRequest(url: URL(string:"https://YourURL.com/admin/registerToken.php")!)
// set the method as POST
request.httpMethod = "POST"
// append the paramter to body
request.httpBody = try! JSONSerialization.data(withJSONObject: tokenString, options: [])
// create the session
URLSession.shared.dataTask(with:request, completionHandler: {(data, response, error) in
if error != nil {
print("There was error during datatask session")
print(error)
} else {
do {
guard let json = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any] else { return }
guard let errors = json?["errors"] as? [[String: Any]] else { return }
if errors.count > 0 {
// show error
print("There is an error during parse JSON datatask")
return
} else {
// show confirmation
print("datatask with JSON format performed successfully")
}
}
}
print(request)
}).resume()
}
Service Side Script(registerToken.php):
<?php
include 'config.php';
$token = $_POST['token'];
$sql = "INSERT INTO users (email,device_id) VALUES ('email','$token')";
mysqli_query($conn, $sql);
mysqli_close($conn);
?>
Running App with real devices log as below:-
Database users table (device id always has nothing):-
users table structure:-
tokenString :-
I had solved this issue by using GET method instead of POST method as below:-
Appdelegate.swift :
var request = URLRequest(url: URL(string:"https://YourURL.com/admin/registerToken.php?token=\(token)")!)
Server side script :
$token = $_GET['token'];
I am trying to connect to my localhost API (that I need to build along with the iOS swift app) that returns a json string. The API is written in Laravel 4 framework.
Here is the iOS Swift code to connect and receive the code:
func checkEmail() {
var request = NSMutableURLRequest(URL: NSURL(string: "http://localhost:3306/laravel/rojectapi/checkEmail"))
var session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
var params = ["email":"myemail#me.com", "password":"password"] as Dictionary
var err: NSError?
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(params, options: nil, error: &err)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
println("Response: \(response)")
var strData = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Body: \(strData)")
var err: NSError?
var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableLeaves, error: &err) as NSDictionary
println("hey")
if(err) {
println(err!.localizedDescription)
}
else {
var success = json["success"] as? Int
println("Success: \(success)")
}
})
task.resume()
}
The Laravel PHP Route:
Route::post('/checkEmail', array(
'as' => 'checkEmail',
'uses' => 'FrontEndController#checkEmail'
));
And then the front-end-controller with the checkEmail method:
public function checkEmail() {
$validator = Validator::make(Input::all(), array(
'email' => 'required|unique:users|email'
));
if($validator->fails()) {
return $validator->messages()->toJson();
} else {
return Response::json(array('success' => true));
}
}
Am I not connecting to the server correctly? I need to do so on my iPhone 5s connected to my laptop, as well as in the emulator. I have tried name.local, localhost:<post> and name.local:<post> in the URL.
Update
I got the code from this tutorial
Either pass NSJSONReadingAllowFragments (.AllowFragments in Swift) or format your JSON properly.
A simple google search revealed that error 3840 is caused due to improperly formatted JSON being passed to the iOS JSON parser.
This occurs if the API is expecting some data from you and you are not able to send it or it is not being received by the APi, try to Echo out the parameter which are received by the API as a response to your iOS Request, It may also occur if you APi Implementation has some error in it, though you may be able to see this error in your Web Console.
I have this error, when i add languages to Localizations in project. When i add language, must change from "localizable strings" to "interface builder storyboard". I mean project of iOS app. Could help you.