Never trust the client browser.
There are four main attack vectors I can see based on this model.
1) Leaked credentials
First, as @JuliePelletier commented, in order for this to work, all the information necessary to make a "charge this card" call to Authorize.Net has to be available to the customer's webbrowser. This includes whatever secure credentials you have to identify you to the processor. With those in hand, anyone can make the same AJAX post to Authorize.Net to charge any card or, worse, refund from your account to any card.
2) Altered order data
Second, as @Jay commented, this scheme requires all the information about the order to pass through the user's browser before getting to your processor. It's trivial for the user to put a breakpoint in the javascript between the return of "get order data" and the call to "send payment data" and alter the variables that are about to be sent. (All modern browsers support this with their built-in developer tools.) This lets the user order $5000 worth of stuff, then successfully charge their card for $0.01. Unless you explicitly checked the amount approved, you'd just see that the payment was successful and be out $4999.99. Even if you added an approved amount check, you'd still have to void off the transaction (which might incur extra fees), cancel the order in your system, and somehow communicate that to the user.
Also, even with a tamper-check, the algorithm for doing that is going to be part of the official Authorize.Net documentation. Unless it involves a secret key which you do not make available to the Javascript (see point 1), it would be very possible to just recalculate the check after making changes. And if the tamper-check is supposed to also validate that the card data is unchanged, there's no way to do that without making it available to the Javascript.
3) Spoofed response
Related to the approved amount check from point 2, your AJAX call is going to be expecting some kind of "Approved" response from Authorize.Net before updating the order in your system to indicate that it was paid. Nothing prevents the user from skipping the call to the processor and faking that response altogether. Then you think you've been paid, but now you have no record of it - not even a failed attempt.
4) Hijacked calls
Finally, none of this provides any protection against a malicious browser plugin hijacking the user's data. You can't prevent one from scraping the card data as the user enters it and just sending it off to a server in Russia (or wherever), but you have no protection against one which intercepts the AJAX POST, redirects the whole thing to that server (including your credentials and the card data), and then either passes it on to Authorize.Net itself or simulates a failure so that the user tries again.
It's via a different attack vector, but this article discusses a recent breach which had a similar effect. In that case, the server was serving a maliciously altered script which caused the card data to first be POSTed to the server, and then sent off to the processor.
From the consumer's point of view the data entered into the iFrame vanishes and the customer is left with an empty iFrame to complete again. It is expected the customer would assume something went wrong with the web page and re-enter and submit their payment details which, on the second attempt, are submitted direct to the Payment Service Provider.
Your site would have similar behavior - the malicious script would POST elsewhere, return what appeared to be an Authorize.Net "please try again" error, and then remove itself. The unsuspecting user would then resubmit, and it would go through, with no indication to you or them that anything untoward happened.
The secure method
The secure way to address all these issues (except for part of 4) is to instead have the user POST their card data to your server. Then, your server sends the request off to Authorize.Net and handles the response. This addresses all the points as follows:
- By sending the request to your processor from the backend, your credentials are never exposed to the user. This also includes any secret data needed for the tamper-check in 2.
- By pulling the data direct from your database to send, you can be sure that it's exactly what you want to be sending, with no chance for the client to modify it.
- Because you're calling Authorize.Net in a way the end user can't see or intercept, you don't have to worry about fake responses being returned.
- POSTing to your own server still doesn't prevent malicious plugins from scraping the user's data as they enter it, and there's really nothing at all you can do about that. Additionally, you still don't have any good way to prevent said malicious plugin from altering your page to POST to some random other server on the internet. But you can verify the IP address posting the data to your server is the same as created the order in the first place, and you can use anti-forgery/anti-XSS tokens to prevent other servers from trying to POST to you.
All that said YOU ARE NOW UNDER HIGHER PCI SCOPE, because you're sending card data to your server. Here's the summary, but you're basically moving yourself from A-EP (Direct POST) to D-Merchant. Even if you just immediately pass the card data along and never store it, you're still in that scope - it just means that you'll have an easier time passing your certification.
An alternative
The other secure way which keeps you out of scope is to outsource the entire payment screen to another site (such as Paypal). This will actually reduce your PCI level, since you have even less interaction with the card data, and it outsources dealing with all these security issues as well.