SquarePointOfSaleSDK failing on callback in Swift Xcode 13.3 - square-connect

I have a tiny Square iOS POS testing app to get the setup working properly but alas, no such luck - it charges $1.00 and the card is correctly processed, but the callback does not happen. The Square app just drops control and gives the screen back to my calling view controller - it does not return to the appDelegate. I have sent this project to Square, hoping for help, but they refuse, saying it is an implementation problem. However it definitely is a problem with Square's documentation, not implementation, as I have set up everything perfectly including the info file, the URL, the appID etc as defined. I am looking for help.
API call follows:
//------ API to Square right here ******-----------------
let callbackURL = URL(string: "POSSDKTest2://")!
var money: SCCMoney?
do {
// Specify the amount of money to charge.
let todaysFeePennies = Int(TodaysFee) * 100
money = try SCCMoney(amountCents: todaysFeePennies, currencyCode: "CAD")
} catch {
print("money conversion failed.")
}
//SCCAPIRequest.setApplicationID(masterInfo.SquareAppID)
SCCAPIRequest.setApplicationID("sq0idp-TbgQGqSrC84qkfcXSTntNg")
var request: SCCAPIRequest?
do
{
let LoginID = "TestPatient"
squareTransDone = false
print("FVC Initiating payment api request")
print("callback: \(callbackURL)")
print("money: \(money!)")
print("LoginID: TestPatient")
print("FVC Initiating payment api request")
request =
try SCCAPIRequest(
callbackURL: callbackURL,
amount: money!,
userInfoString: LoginID,
locationID: "",
notes: "Testing SDK",
customerID: nil, // used by Square, not us
supportedTenderTypes: .all,
clearsDefaultFees: false,
returnsAutomaticallyAfterPayment: false,
//.true makes no difference!
disablesKeyedInCardEntry: false,
skipsReceipt: false)
} catch {
print("SCCAPIRequest croaked.")
}
// Open Point of Sale to complete the payment.
var success = false
do {
try SCCAPIConnection.perform(request!)
success = true
print("api success!")//this actually shows in the log
} catch let error as NSError {
print("Error detected: \(error.localizedDescription)")
}//end API
}//------------------------------------
The calling view controller has a screen with a button saying "Post $1.00 payment" which comes to this code. The log shows "api success" and no error message.
Image of app's info.plist attached showing setup for Square.
info.plist screenshot
Image of Credentials from Square Developer page attached.
Credentials page

Related

AWS Chime SDK js does not recognize video and audio elements

I am attempting to get the basic tutorial for the AWS Chime SDK to work in our application and the meetingSession.audioVideo.listVideoInputDevices() always returns nothing/null.
I am running this on lastest chrome, my operating system is a windows 10 workspace instance. I have headphones plugged in; but that shouldn't make a difference.
My expected result is to return at least one device for the video. Here is the output from the Logger.
2020-08-26T15:29:19.127Z [INFO] MyLogger - attempting to trigger media device labels since they are hidden
chime-bundle.js:1 2020-08-26T15:29:19.133Z [INFO] MyLogger - unable to get media device labels
chime-bundle.js:1 2020-08-26T15:29:19.134Z [INFO] MyLogger - API/DefaultDeviceController/listVideoInputDevices null -> []
chime-bundle.js:1 Uncaught (in promise) TypeError: Cannot read property 'deviceId' of undefined
*Note. The video and audio elements are not hidden.
I have tried the code snippits from various demos. Which are all just a copy of AWS's walkthrough. So pretty much zero information. I have researched how the audio devices work in html5 and looking through the files provided in the sdk-js, I am even more confused. Can someone point me in the right direction?
Here is the basic code, you can get it, and a description from the link above.
var fetchResult = await window.fetch(
window.encodeURI("<our endpoint for backend (running c# instead of node)>",
{
method: 'POST'
}
);
let result = await fetchResult.json();
console.log("Result from Chime API:", result);
const logger = new ConsoleLogger('MyLogger', LogLevel.INFO);
const deviceController = new DefaultDeviceController(logger);
const meetingResponse = result.JoinInfo.Meeting;
const attendeeResponse = result.JoinInfo.Attendee;
const configuration = new MeetingSessionConfiguration(meetingResponse, attendeeResponse);
// In the usage examples below, you will use this meetingSession object.
const meetingSession = new DefaultMeetingSession(
configuration,
logger,
deviceController
);
console.log("MEETING SESSION", meetingSession);
//SETUP AUDIO
const audioElement = document.getElementById('notary-audio');
meetingSession.audioVideo.bindAudioElement(audioElement);
const videoElement = document.getElementById('notary-video');
// Make sure you have chosen your camera. In this use case, you will choose the first device.
const videoInputDevices = await meetingSession.audioVideo.listVideoInputDevices();
// The camera LED light will turn on indicating that it is now capturing.
// See the "Device" section for details.
await meetingSession.audioVideo.chooseVideoInputDevice(videoInputDevices[0].deviceId);
const observer = {
audioVideoDidStart: () => {
console.log('Started');
},
audioVideoDidStop: sessionStatus => {
// See the "Stopping a session" section for details.
console.log('Stopped with a session status code: ', sessionStatus.statusCode());
},
audioVideoDidStartConnecting: reconnecting => {
if (reconnecting) {
// e.g. the WiFi connection is dropped.
console.log('Attempting to reconnect');
}
},
// videoTileDidUpdate is called whenever a new tile is created or tileState changes.
videoTileDidUpdate: tileState => {
// Ignore a tile without attendee ID and other attendee's tile.
if (!tileState.boundAttendeeId || !tileState.localTile) {
return;
}
// videoTileDidUpdate is also invoked when you call startLocalVideoTile or tileState changes.
console.log(`If you called stopLocalVideoTile, ${tileState.active} is false.`);
meetingSession.audioVideo.bindVideoElement(tileState.tileId, videoElement);
localTileId = tileState.tileId;
},
videoTileWasRemoved: tileId => {
if (localTileId === tileId) {
console.log(`You called removeLocalVideoTile. videoElement can be bound to another tile.`);
localTileId = null;
}
}
};
meetingSession.audioVideo.addObserver(observer);
meetingSession.audioVideo.start();

nativescript-phone prevents Nativescript-contacts from returning

I have an app where I want to select a person from contacts and then send a text to that person. It works as expected for the first user, but after that the app never receives control after the contact is selected. I've isolated the problem to the Nativescript-phone plugin. If you simply call phone.sms() to send a text, and then call contacts.getContact(), the problem occurs. I see this on both Android and iOS.
I've created a sample app that demos the problem at https://github.com/dlcole/contactTester. The sample app is Android only. I've spent a couple days on this and welcome any insights.
Edit 4/21/2020:
I've spent more time on this and can see what's happening. Both plugins have the same event handler and same request codes:
nativescript-phone:
var SEND_SMS = 1001;
activity.onActivityResult = function(requestCode, resultCode, data) {
nativescript-contacts:
var PICK_CONTACT = 1001;
appModule.android.on("activityResult", function(eventData) {
What happens is that after invoking phone.sms, calling contacts.getContact causes control to return to the phone plugin, and NOT the contacts plugin. I tried changing phone's request code to 1002 but had the same results.
So, the next step is to determine how to avoid the collision of the event handlers.
Instead of using activityResult event, nativescript-phone plugin overwrites the default activity result callback.
A workaround is to set the callback to it's original value after you are done with nativescript-phone.
exports.sendText = function (args) {
console.log("entering sendText");
const activity = appModule.android.foregroundActivity || appModule.android.startActivity;
const onActivityResult = activity.onActivityResult;
permissions.requestPermissions([android.Manifest.permission.CALL_PHONE],
"Permission needed to send text")
.then(() => {
console.log("permission granted");
phone.sms()
.then((result) => {
console.log(JSON.stringify(result, null, 4));
activity.onActivityResult = onActivityResult;
})
})
}

Making a GET request with swift 4 works in playgrounds but not in project

Using Swift 4 and Xcode 10
I am trying to make a GET request to an API and get the results in json, and my code works perfectly in my playground, but when I copy it over to my app, I get a "Program ended with exit code: 0" error.
I'd like to make this a function I can call and change up the headers, httpMethod, credentials, actionURL, etc.. so I can reuse it for different calls to this API.
This is my first shot at this, and have been searching all over.
1) This is where I borrowed most of the code for this part of the project. Error parsing JSON in swift and loop in array
2) I tried using the advice in this video to build a struct for the data. https://www.youtube.com/watch?v=WwT2EyAVLmI&t=105s
Not sure if it is something on the swift side, or the xcode side...
import Foundation
import Cocoa
// removed in project, works in playgrounds
//import PlaygroundSupport
func makeGetCall() {
// Set up the URL request
let baseURL: String = "https://ws.example.net/v1/action"
guard let url = URL(string: baseURL) else {
print("Error: cannot create URL")
return
}
// set up the session
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
// set up auth
let token = "MYTOKEN"
let key = "MYKEY"
let loginString = String(format: "%#:%#", token, key)
let loginData = loginString.data(using: String.Encoding.utf8)?.base64EncodedString()
// make the request
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("Basic \(loginData!)", forHTTPHeaderField: "Authorization")
let task = session.dataTask(with: request) {
(data, response, error) in
// check for any errors
guard error == nil else {
print("error calling GET")
print(error!)
return
}
// make sure we got data
guard let responseData = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
do {
guard let apiResponse = try JSONSerialization.jsonObject(with: responseData, options: [])
as? [String: Any] else {
print("error trying to convert data to JSON")
return
}
// let's just print it to prove we can access it
print(apiResponse)
// the apiResponse object is a dictionary
// so we just access the title using the "title" key
// so check for a title and print it if we have one
//guard let todoTitle = todo["title"] as? String else {
// print("Could not get todo title from JSON")
// return
//}
//print("The title is: " + todoTitle)
} catch {
print("error trying to convert data to JSON")
return
}
}
task.resume()
}
makeGetCall()
// removed in project, works in playgrounds
//PlaygroundPage.current.needsIndefiniteExecution = true
In playgrounds I receive the json response as expected, but when I copy the code over to my project I receive an error.
Expected output example:
["facet": <__NSArrayI 0x7fb4305d1b40>(
asnNumber,
assetPriority,
assetType,
etc...
"Program ended with exit code: 0" is not an error; it just means the program came to an end. In fact, it is the opposite of an error, since it means the program came to an end in good order.
So, what kind of project template did you use? I'm guessing you used a Cocoa command-line tool. In that case, the problem is that simply you have no runloop, so we reach the last line and come to an end before any asynchronous stuff (e.g. networking) can take place.
This is exactly parallel to what you had to do in your playground. You couldn't network asynchronously without needsIndefiniteExecution; the playground needs to keep running after it has reached the last line, to give the networking a chance to happen. In the same way, you need the runloop in the command-line tool to keep going after it has reached the last line, to give the networking a chance to happen.
For how to give your command-line tool a runloop, see for example Run NSRunLoop in a Cocoa command-line program
Alternatively, don't use a command-line tool. Make an actual app. Now the app persists (until you quit it), and asynchronous networking just works.

Google+ insert moment using google-api-dotnet-client

I am trying to write an activity in Google+ using the dotnet-client. The issue is that I can't seem to get the configuration of my client app correctly. According to the Google+ Sign-In configuration and this SO question we need to add the requestvisibleactions parameter. I did that but it did not work. I am using the scope https://www.googleapis.com/auth/plus.login and I even added the scope https://www.googleapis.com/auth/plus.moments.write but the insert still did not work.
This is what my request url looks like:
https://accounts.google.com/ServiceLogin?service=lso&passive=1209600&continue=https://accounts.google.com/o/oauth2/auth?scope%3Dhttps://www.googleapis.com/auth/plus.login%2Bhttps://www.googleapis.com/auth/plus.moments.write%26response_type%3Dcode%26redirect_uri%3Dhttp://localhost/%26state%3D%26requestvisibleactions%3Dhttp://schemas.google.com/AddActivity%26client_id%3D000.apps.googleusercontent.com%26request_visible_actions%3Dhttp://schemas.google.com/AddActivity%26hl%3Den%26from_login%3D1%26as%3D-1fbe06f1c6120f4d&ltmpl=popup&shdf=Cm4LEhF0aGlyZFBhcnR5TG9nb1VybBoADAsSFXRoaXJkUGFydHlEaXNwbGF5TmFtZRoHQ2hpa3V0bwwLEgZkb21haW4aB0NoaWt1dG8MCxIVdGhpcmRQYXJ0eURpc3BsYXlUeXBlGgdERUZBVUxUDBIDbHNvIhTeWybcoJ9pXSeN2t-k8A4SUbfhsygBMhQivAmfNSs_LkjXXZ7bPxilXgjMsQ&scc=1
As you can see from there that there is a request_visible_actions and I even added one that has no underscore in case I got the parameter wrong (requestvisibleactions).
Let me say that my app is being authenticated successfully by the API. I can get the user's profile after being authenticated and it is on the "insert moment" part that my app fails. My insert code:
var body = new Moment();
var target = new ItemScope();
target.Id = referenceId;
target.Image = image;
target.Type = "http://schemas.google.com/AddActivity";
target.Description = description;
target.Name = caption;
body.Target = target;
body.Type = "http://schemas.google.com/AddActivity";
var insert =
new MomentsResource.InsertRequest(
// this is a valid service instance as I am using this to query the user's profile
_plusService,
body,
id,
MomentsResource.Collection.Vault);
Moment result = null;
try
{
result = insert.Fetch();
}
catch (ThreadAbortException)
{
// User was not yet authenticated and is being forwarded to the authorization page.
throw;
}
catch (Google.GoogleApiRequestException requestEx)
{
// here I get a 401 Unauthorized error
}
catch (Exception ex)
{
} `
For the OAuth flow, there are two issues with your request:
request_visible_actions is what is passed to the OAuth v2 server (don't pass requestvisibleactions)
plus.moments.write is a deprecated scope, you only need to pass in plus.login
Make sure your project references the latest version of the Google+ .NET client library from here:
https://developers.google.com/resources/api-libraries/download/stable/plus/v1/csharp
I have created a project on GitHub showing a full server-side flow here:
https://github.com/gguuss/gplus_csharp_ssflow
As Brettj said, you should be using the Google+ Sign-in Button as demonstrated in the latest Google+ samples from here:
https://github.com/googleplus/gplus-quickstart-csharp
First, ensure you are requesting all of the activity types you're writing. You will know this is working because the authorization dialog will show "Make your app activity available via Google, visible to you and: [...]" below the text that starts with "This app would like to". I know you checked this but I'm 90% sure this is why you are getting the 401 error code. The following markup shows how to render the Google+ Sign-In button requesting access to Add activities.
<div id="gConnect">
<button class="g-signin"
data-scope="https://www.googleapis.com/auth/plus.login"
data-requestvisibleactions="http://schemas.google.com/AddActivity"
data-clientId="YOUR_CLIENT_ID"
data-accesstype="offline"
data-callback="onSignInCallback"
data-theme="dark"
data-cookiepolicy="single_host_origin">
</button>
Assuming you have a PlusService object with the correct activity type set in data-requestvisibleactions, the following code, which you should be able to copy/paste to see it work, concisely demonstrates writing moments using the .NET client and has been tested to work:
Moment body = new Moment();
ItemScope target = new ItemScope();
target.Id = "replacewithuniqueforaddtarget";
target.Image = "http://www.google.com/s2/static/images/GoogleyEyes.png";
target.Type = "";
target.Description = "The description for the activity";
target.Name = "An example of add activity";
body.Target = target;
body.Type = "http://schemas.google.com/AddActivity";
MomentsResource.InsertRequest insert =
new MomentsResource.InsertRequest(
_plusService,
body,
"me",
MomentsResource.Collection.Vault);
Moment wrote = insert.Fetch();
Note, I'm including Google.Apis.Plus.v1.Data for convenience.
Ah it's that simple! Maybe not? I am answering my own question and consequently accept it as the answer (after a few days of course) so others having the same issue may be guided. But I will definitely up-vote Gus' answer for it led me to the fix for my code.
So according to #class answer written above and as explained on his blog the key to successfully creating a moment is adding the request_visible_actions parameter. I did that but my request still failed and it is because I was missing an important thing. You need to add one more parameter and that is the access_type and it should be set to offline. The OAuth request, at a minimum, should look like: https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/plus.login&response_type=code&redirect_uri=http://localhost/&request_visible_actions=http://schemas.google.com/AddActivity&access_type=offline.
For the complete and correct client code you can get Gus' example here or download the entire dotnet client library including the source and sample and add what I added below. The most important thing that you should remember is modifying your AuthorizationServerDescription for the Google API. Here's my version of the authenticator:
public static OAuth2Authenticator<WebServerClient> CreateAuthenticator(
string clientId, string clientSecret)
{
if (string.IsNullOrWhiteSpace(clientId))
throw new ArgumentException("clientId cannot be empty");
if (string.IsNullOrWhiteSpace(clientSecret))
throw new ArgumentException("clientSecret cannot be empty");
var description = GoogleAuthenticationServer.Description;
var uri = description.AuthorizationEndpoint.AbsoluteUri;
// This is the one that has been documented on Gus' blog site
// and over at Google's (https://developers.google.com/+/web/signin/)
// This is not in the dotnetclient sample by the way
// and you need to understand how OAuth and DNOA works.
// I had this already, see my original post,
// I thought it will make my day.
if (uri.IndexOf("request_visible_actions") < 1)
{
var param = (uri.IndexOf('?') > 0) ? "&" : "?";
description.AuthorizationEndpoint = new Uri(
uri + param +
"request_visible_actions=http://schemas.google.com/AddActivity");
}
// This is what I have been missing!
// They forgot to tell us about this or did I just miss this somewhere?
uri = description.AuthorizationEndpoint.AbsoluteUri;
if (uri.IndexOf("offline") < 1)
{
var param = (uri.IndexOf('?') > 0) ? "&" : "?";
description.AuthorizationEndpoint =
new Uri(uri + param + "access_type=offline");
}
// Register the authenticator.
var provider = new WebServerClient(description)
{
ClientIdentifier = clientId,
ClientSecret = clientSecret,
};
var authenticator =
new OAuth2Authenticator<WebServerClient>(provider, GetAuthorization)
{ NoCaching = true };
return authenticator;
}
Without the access_type=offline my code never worked and it will never work. Now I wonder why? It would be good to have some explanation.

Need help utilizing Beeblex validating in-app purchases with xCode

Has anyone had success using Beeblex to validate in-app purchases? I have an app that is already live in the app-store with in-app purchases enabled and working. In light of the recent hacking news concerning in-app purchases I am attempting to update it to use Beeblex to validate purchases. The problem is that Beeblex always returns a result saying that Apple claims the purchase to be invalid. I am hoping someone on here is successfully using Beeblex and can post a working example. I am following their advice to let the purchase go completely through, then validate it. I basically just cut and pasted their code right off their website, so not sure what I could do differently.
- (void) validatePurchaseOrRestore
{
//The transaction has been reported as complete for a new purchase of the upgrade. I now make use of Beeblex
//to verify the receipt to ensure the purchase is legit.
if (![BBXIAPTransaction canValidateTransactions])
{
transactionResult = 4;
return; // There is no connectivity to reach the server.
// You should try the validation at a later date.
}
BBXIAPTransaction *bbxTransaction = [[BBXIAPTransaction alloc] initWithTransaction:transactionCopy];
bbxTransaction.useSandbox;
[bbxTransaction validateWithCompletionBlock:^(NSError *error)
{
if (bbxTransaction.transactionVerified)
{
if (bbxTransaction.transactionIsDuplicate)
{
// The transaction is valid, but duplicate - it has already been
// sent to Beeblex in the past.
transactionResult = 1;
}
else
{
// The transaction has been successfully validated
// and is unique.
NSLog(#"Transaction data: %#", bbxTransaction.validatedTransactionData);
transactionResult = 0;
}
}
else
{
// Check whether this is a validation error, or if something
// went wrong with Beeblex.
if (bbxTransaction.hasServerError)
{
// The error was not caused by a problem with the data, but is
// most likely due to some transient networking issues.
transactionResult = 4;
}
else
{
// The transaction supplied to the validation service was not valid according to Apple.
transactionResult = 3;
purchaseDidFail = TRUE;
}
}
}];
}

Resources